#include "cam_tele.h"
#include "cam_tele_c.h"
#include "elog.h"
#include "elog_file.h"
#include "ws_response.h"
#include "ws_constant.h"
#include "dwarf_utils.h"
#include "state_manager.h"

#include "rkmedia_venc.h"
#include "rkmedia_api.h"
#include "sample_common.h"
#include "cam_tele_mpi_enc.h"
#include "album_common.h"
#include "img_utils.h"
#include "album_picture.h"
#include "album_burst.h"
#include "album_video.h"
#include "im2d.h"
#include "rga.h"
#include "timer_manager.h"
#include "album.h"
#include "ws_server_c.h"
#include "timer_utils.h"
#include "image_processor/preprocessor.h"
#include "ws_version.h"

#include "opencv2/opencv.hpp"

#include <unistd.h>
#include <stdio.h>
#include <linux/videodev2.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/ioctl.h>

#include <atomic>
#include <chrono>
#include <iostream>
#include <iomanip>
#include <condition_variable>
#include <thread>
#include <functional>
#include <future>
#include <unordered_map>
#include <mutex>
#include <fstream>
#include <sstream>
#include <string>
#include <map>
#include <climits>
#include <filesystem>

using namespace std;
namespace fs = std::filesystem;

//IRCUT
#define IRCUT_DEV "/dev/v4l-subdev5"
#define H264_RECORD_BITRATE 20 * 1024 * 1024
#define H264_RECORD_FPS 27

//摄像头相关参数
typedef struct {
    RK_U32 src_width = 3840;
    RK_U32 src_height = 2160;
    IMAGE_TYPE_E pix_fmt = IMAGE_TYPE_NV12;
    const RK_CHAR *pc_video_node = "rkispp_scale0";
    const RK_CHAR *pc_iqfile_dir = "/oem/etc/iqfiles/";
    RK_S32 cam_id = 0;
    RK_U32 rotation = 0;
    RK_U32 flip = 1;
    RK_U32 vi_buf_count = 3;
} cam_tele_attr_t;

static cam_tele_attr_t st_cam_attr;
static cam_tele_attr_t st_cam_attr_binning = {
    1920,
    1080,
    IMAGE_TYPE_NV12,
    "rkispp_scale0",
    "/oem/etc/iqfiles/binning",
    0,
    0,
    1,
    3
};
static int jpeg_preview_quality = 60;
static int jpeg_defalut_quality = 85;
static  VENC_ROTATION_E enRotation = VENC_ROTATION_0;
static MPP_CHN_S stSrcChn;
static MPP_CHN_S stDestChn;
static MUXER_CHN_S stMuxerChn;
static mutex camera_mutex;
static mutex feature_param_mutex;
static std::atomic<bool> b_cam_open = false;
static std::atomic<bool> b_binning = false;
static CamTelePhotoState photo_state = CAM_TELE_PHOTO_IDLE;
static CamTeleParamsT cam_tele_params;

static StateManager* state_manager = StateManager::GetInstance();

static const string photo_name_prefix = "DWARF_TELE_";                   //照片文件名前缀
static const string burst_photo_path_prefix = "DWARF_TELE_BURST_";       //连拍文件夹前缀
static const string record_name_prefix = "DWARF_TELE_";                  //视频文件名前缀
static const string timelapse_name_prefix = "DWARF_TELE_TL_";            //延时摄影文件名前缀 

//拿图相关
static std::atomic<bool> b_get_media_buffer = true;
static std::atomic<bool> b_get_media_buffer_bypass = true;
static std::future<void> get_media_buffer_t;
static std::future<void> get_media_buffer_bypass_t;

//拍照相关
static VENC_RECV_PIC_PARAM_S st_recv_param;
static RK_BOOL b_start_recv_frame = RK_FALSE;       //jpeg编码通道开始编码
static string photo_path;                   
static int burst_photo_count = 3;                   //连拍默认数量
static int burst_photo_id = 0;                      //连拍图片id
static string burst_photo_path;
static int burst_photo_interval = 0;   

//录像相关
static CamTeleRecordState record_state = CAM_RECORD_IDLE;
static TimerManager record_timer_manager = TimerManager();
static string m_video_path;
static int record_time;
static std::atomic<int> record_loading_count = 0;

//延时摄影相关
static MEDIA_BUFFER tl_nv12_mb = NULL;
static pthread_cond_t  tl_nv12_mb_cond = PTHREAD_COND_INITIALIZER;
static CamTeleTimelapseState timelapse_state = CAM_TIMELAPSE_IDLE;
static uint8_t* timelapse_nv12_ptr = NULL;
static int timelapse_nv12_size;
static TimerManager timelapse_timer_manager = TimerManager();
static int timelapse_interval = 1;
static int timelapse_out_time = 1;    // 已成片时间
static int timelapse_elapsed_time = 1;

// 全景相关
static CamTelePanoramaState panorama_state = CAM_PANORAMA_IDLE;

//jpg图传相关
static std::vector<unsigned char> jpg_buff;
static uint8_t* jpg_ptr = NULL;
static int jpg_size;
static unsigned int jpg_frame_id = 0;
static pthread_mutex_t jpg_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  jpg_cond = PTHREAD_COND_INITIALIZER;
// static uint8_t* img_buffer = NULL;
// static int buffer_size = 0;

// 摄像头nv12图像数据,2k
static MEDIA_BUFFER nv12_mb = NULL;
static uint64_t nv12_nv_frame_id = 0;
static pthread_mutex_t nv12_mb_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  nv12_mb_cond = PTHREAD_COND_INITIALIZER;
// 摄像头nv12图像数据, binning情况下2k,非binning情况下4k
static MEDIA_BUFFER nv12_mb_bypass = NULL;
static pthread_mutex_t nv12_mb_bypass_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t  nv12_mb_bypass_cond = PTHREAD_COND_INITIALIZER;

// 抓RAW图
static int fd_raw_ = -1;
static std::atomic<bool> b_capture_raw = false;
static const cv::Size img_size_2k = cv::Size(1920, 1080);
static const cv::Size img_size_4k = cv::Size(3840, 2160);
static struct v4l2_format vformat = {0};
static struct v4l2_requestbuffers reqbuf = {0};
static struct v4l2_buffer buffer = {0};
static struct v4l2_plane planes[1];
static struct data_buffer {
    void* start;
    unsigned int length;
} *buffers;
static cv::Mat img_raw_;
static std::mutex img_raw_mutex;
static std::condition_variable img_raw_cv;

// 相册管理
static Album m_album = Album();

// 曝光
static bool b_auto_exposure = true;
static std::vector<ExposureSetting> exp_settings = {
    {0, 0.0001, "1/10000"},
    {3, 0.000125, "1/8000"},
    {6, 0.00015625, "1/6400"},
    {9, 0.0002, "1/5000"},
    {12, 0.00025, "1/4000"},
    {15, 0.0003125, "1/3200"},
    {18, 0.0004, "1/2500"},
    {21, 0.0005, "1/2000"},
    {24, 0.000625, "1/1600"},
    {27, 0.0008, "1/1250"},
    {30, 0.001, "1/1000"},
    {33, 0.00125, "1/800"},
    {36, 0.0015625, "1/640"},
    {39, 0.002, "1/500"},
    {42, 0.0025, "1/400"},
    {45, 0.003125, "1/320"},
    {48, 0.004, "1/250"},
    {51, 0.005, "1/200"},
    {54, 0.00625, "1/160"},
    {57, 0.008, "1/125"},
    {60, 0.01, "1/100"},
    {63, 0.0125, "1/80"},
    {66, 0.0166666667, "1/60"},
    {69, 0.02, "1/50"},
    {72, 0.025, "1/40"},
    {75, 0.0333333333, "1/30"},
    {78, 0.04, "1/25"},
    {81, 0.05, "1/20"},
    {84, 0.0666666667, "1/15"},
    {87, 0.0769230769, "1/13"},
    {90, 0.1, "1/10"},
    {93, 0.125, "1/8"},
    {96, 0.1666666667, "1/6"},
    {99, 0.2, "1/5"},
    {102, 0.25, "1/4"},
    {105, 0.3333333333, "1/3"},
    {108, 0.4, "0.4"},
    {111, 0.5, "0.5"},
    {114, 0.6, "0.6"},
    {117, 0.8, "0.8"},
    {120, 1.0, "1"},
    {123, 1.3, "1.3"},
    {126, 1.6, "1.6"},
    {129, 2.0, "2"},
    {132, 2.5, "2.5"},
    {135, 3.2, "3.2"},
    {138, 4.0, "4"},
    {141, 5.0, "5"},
    {144, 6.0, "6"},
    {147, 8.0, "8"},
    {150, 10.0, "10"},
    {153, 13.0, "13"},
    {156, 15.0, "15"}
};

// 增益
static bool b_auto_gain = true;
static std::vector<IntValueSetting> gain_settings = {
    {0, 0},
    {3, 10},
    {6, 20},
    {9, 30},
    {12, 40},
    {15, 50},
    {18, 60},
    {21, 70},
    {24, 80},
    {27, 90},
    {30, 100},
    {33, 110},
    {36, 120},
    {39, 130},
    {42, 140},
    {45, 150},
    {48, 160},
    {51, 170},
    {54, 180},
    {57, 190},
    {60, 200},
    {63, 210},
    {66, 220},
    {69, 230},
    {72, 240}
};

// 白平衡色温值
static std::vector<IntValueSetting> wb_settings = {
    {0, 2800},
    {3, 2900},
    {6, 3000},
    {9, 3100},
    {12, 3200},
    {15, 3300},
    {18, 3400},
    {21, 3500},
    {24, 3600},
    {27, 3700},
    {30, 3800},
    {33, 3900},
    {36, 4000},
    {39, 4100},
    {42, 4200},
    {45, 4300},
    {48, 4400},
    {51, 4500},
    {54, 4600},
    {57, 4700},
    {60, 4800},
    {63, 4900},
    {66, 5000},
    {69, 5100},
    {72, 5200},
    {75, 5300},
    {78, 5400},
    {81, 5500},
    {84, 5600},
    {87, 5700},
    {90, 5800},
    {93, 5900},
    {96, 6000},
    {99, 6100},
    {102, 6200},
    {105, 6300},
    {108, 6400},
    {111, 6500},
    {114, 6600},
    {117, 6700},
    {120, 6800},
    {123, 6900},
    {126, 7000},
    {129, 7100},
    {132, 7200},
    {135, 7300},
    {138, 7400},
    {141, 7500}
};

//函数指针表
static unordered_map<int, int (CamTele::*)(WsPacket& ws_packet)> camera_func_table {
    { CMD_CAMERA_TELE_OPEN_CAMERA, &CamTele::openCamera },
    { CMD_CAMERA_TELE_CLOSE_CAMERA, &CamTele::closeCamera },
    { CMD_CAMERA_TELE_PHOTOGRAPH, &CamTele::photograph},
    { CMD_CAMERA_TELE_BURST, &CamTele::burstPhotograph},
    { CMD_CAMERA_TELE_STOP_BURST, &CamTele::stopBurstPhoto},
    { CMD_CAMERA_TELE_START_RECORD, &CamTele::startRecord},
    { CMD_CAMERA_TELE_STOP_RECORD, &CamTele::stopRecord},
    { CMD_CAMERA_TELE_SET_EXP_MODE, &CamTele::setExpMode},
    { CMD_CAMERA_TELE_GET_EXP_MODE, &CamTele::getExpMode},
    { CMD_CAMERA_TELE_SET_EXP, &CamTele::setExp},
    { CMD_CAMERA_TELE_GET_EXP, &CamTele::getExp},
    // { CMD_CAMERA_TELE_SET_GAIN_MODE, &CamTele::setGainMode },
    // { CMD_CAMERA_TELE_GET_GAIN_MODE, &CamTele::getGainMode },
    { CMD_CAMERA_TELE_SET_GAIN, &CamTele::setGain },
    { CMD_CAMERA_TELE_GET_GAIN, &CamTele::getGain },
    { CMD_CAMERA_TELE_SET_BRIGHTNESS, &CamTele::setBrightness },
    { CMD_CAMERA_TELE_GET_BRIGHTNESS, &CamTele::getBrightness },
    { CMD_CAMERA_TELE_SET_CONTRAST, &CamTele::setContrast },
    { CMD_CAMERA_TELE_GET_CONTRAST, &CamTele::getContrast },
    { CMD_CAMERA_TELE_SET_SATURATION, &CamTele::setSaturation },
    { CMD_CAMERA_TELE_GET_SATURATION, &CamTele::getSaturation },
    { CMD_CAMERA_TELE_SET_HUE, &CamTele::setHue },
    { CMD_CAMERA_TELE_GET_HUE, &CamTele::getHue },
    { CMD_CAMERA_TELE_SET_SHARPNESS, &CamTele::setSharpness },
    { CMD_CAMERA_TELE_GET_SHARPNESS, &CamTele::getSharpness },
    { CMD_CAMERA_TELE_SET_WB_MODE, &CamTele::setWBMode },
    { CMD_CAMERA_TELE_GET_WB_MODE, &CamTele::getWBMode },
    { CMD_CAMERA_TELE_SET_WB_SCENE, &CamTele::setWBScene },
    { CMD_CAMERA_TELE_GET_WB_SCENE, &CamTele::getWBScene },
    { CMD_CAMERA_TELE_SET_WB_CT, &CamTele::setWBCT },
    { CMD_CAMERA_TELE_GET_WB_CT, &CamTele::getWBCT },
    { CMD_CAMERA_TELE_SET_IRCUT, &CamTele::setIrCut },
    { CMD_CAMERA_TELE_GET_IRCUT, &CamTele::getIrCut },
    { CMD_CAMERA_TELE_START_TIMELAPSE_PHOTO, &CamTele::startTimeLapsePhotograph },
    { CMD_CAMERA_TELE_STOP_TIMELAPSE_PHOTO, &CamTele::stopTimeLapsePhotograph },
    { CMD_CAMERA_TELE_SET_ALL_PARAMS, &CamTele::setAllParams },
    { CMD_CAMERA_TELE_GET_ALL_PARAMS, &CamTele::getAllParams },
    { CMD_CAMERA_TELE_SET_FEATURE_PARAM, &CamTele::setFeatureParam },
    { CMD_CAMERA_TELE_GET_ALL_FEATURE_PARAMS, &CamTele::getAllFeatureParams},
    { CMD_CAMERA_TELE_GET_SYSTEM_WORKING_STATE, &CamTele::getSystemWorkingState},
    { CMD_CAMERA_TELE_SET_JPG_QUALITY, &CamTele::setJpgQuality }
};

CamTele::CamTele() {
}

CamTele::~CamTele() {

}

int CamTele::handleMessage(WsPacket& ws_packet) {
    log_i("handle camtele message, cmd = %d", ws_packet.cmd());

    //在表中查找函数指针并调用
    auto it = camera_func_table.find(ws_packet.cmd());
    if (it != camera_func_table.end()) {
        (this->*(it->second))(ws_packet);
    }
    else {
        log_i("Function not found");
    }

    return 0;
}

int matToByteArray(const cv::Mat& mat, int jpg_quality) {
    if (mat.empty()) 
        return -1;

    std::vector<int> param = std::vector<int>(2);
    param[0] = cv::IMWRITE_JPEG_QUALITY;
    param[1] = jpg_quality;  // default(95) 0-100

    pthread_mutex_lock(&jpg_mutex);
    
    jpg_buff.clear();

    cv::imencode(".jpg", mat, jpg_buff, param);

    jpg_size = jpg_buff.size();
    jpg_ptr = jpg_buff.data();

    jpg_frame_id++;
    
    pthread_cond_broadcast(&jpg_cond);
    pthread_mutex_unlock(&jpg_mutex);

    return 0;
}

void getJgpFrame(uint8_t* &frame, int &frame_size, unsigned int &frame_id) {
    pthread_mutex_lock(&jpg_mutex);
    if (frame_id == jpg_frame_id)
        pthread_cond_wait(&jpg_cond, &jpg_mutex);// 如果当前帧已经发送了，则等待下一帧，否则直接发送当前帧
    frame = (uint8_t*)malloc(jpg_size);
    frame_size = jpg_size;
    frame_id = jpg_frame_id;
    // log_i("cam tele get jpg size = %d", frame_size);
    memcpy(frame, jpg_ptr, jpg_size);
    pthread_mutex_unlock(&jpg_mutex);
}

// 延时摄影用
void getCamTeleNv12(uint8_t** frame, int* frame_size) {
    if (b_cam_open) {
        pthread_mutex_lock(&nv12_mb_bypass_mutex);
        timelapse_nv12_size = RK_MPI_MB_GetSize(nv12_mb_bypass);
        timelapse_nv12_ptr = (uint8_t*)RK_MPI_MB_GetPtr(nv12_mb_bypass);
        *frame = (uint8_t*)malloc(timelapse_nv12_size);
        *frame_size = timelapse_nv12_size;
    
        memcpy(*frame, timelapse_nv12_ptr, timelapse_nv12_size);
        pthread_mutex_unlock(&nv12_mb_bypass_mutex);
    }  
}

void CamTele::getMediaBuffer() {
    int ret = 0;
    MEDIA_BUFFER mb = NULL;
    time_checker timer;

    nv12_nv_frame_id = 0;
    while (b_get_media_buffer) {
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, VI_CHANNEL_RKISPP_SCALE0, 100);
        if (!mb) {
            // log_i("RK_MPI_SYS_GetMediaBuffer get null buffer");
            continue;
        }
        pthread_mutex_lock(&nv12_mb_mutex);
        rkfree(nv12_mb);
        nv12_mb = RK_MPI_MB_Copy(mb, RK_TRUE);
        nv12_nv_frame_id += 1;
        rkfree(mb);
        pthread_cond_broadcast(&nv12_mb_cond);
        pthread_mutex_unlock(&nv12_mb_mutex);
    }	
    rkfree(nv12_mb);
    RK_MPI_SYS_StopGetMediaBuffer(RK_ID_VI, VI_CHANNEL_RKISPP_SCALE0);
}

/*
    buffer需要在调用后释放，不会等待当前曝光的图到达，直接返回上一张图
*/
MEDIA_BUFFER CamTele::getNv12MediaBuffer() {
    MEDIA_BUFFER buffer = NULL;
    time_checker timer;

    if (b_get_media_buffer) {
        pthread_mutex_lock(&nv12_mb_mutex);
        buffer = RK_MPI_MB_Copy(nv12_mb, RK_TRUE);
        pthread_mutex_unlock(&nv12_mb_mutex);
    }

    return buffer;
}

/*
    buffer需要在调用后释放，不会等待当前曝光的图到达，直接返回上一张图
*/
void CamTele::getNv12MediaBufferWithID(MEDIA_BUFFER* buffer, uint64_t* frame_id) {
    time_checker timer;

    if (b_get_media_buffer) {
        pthread_mutex_lock(&nv12_mb_mutex);
        *buffer = RK_MPI_MB_Copy(nv12_mb, RK_TRUE);
        *frame_id = nv12_nv_frame_id;
        pthread_mutex_unlock(&nv12_mb_mutex);
    }

    return ;
}


/*
    buffer需要在调用后释放，会等待当前曝光的图到达
*/
MEDIA_BUFFER CamTele::getNewestNv12MediaBuffer() {
    MEDIA_BUFFER buffer = NULL;
    time_checker timer;

    if (b_get_media_buffer) {
        pthread_mutex_lock(&nv12_mb_mutex);
        pthread_cond_wait(&nv12_mb_cond, &nv12_mb_mutex);
        buffer = RK_MPI_MB_Copy(nv12_mb, RK_TRUE);
        pthread_mutex_unlock(&nv12_mb_mutex);
    }

    return buffer;
}

/// @brief 获取RGB图像数据，分辨率2k,包括格式转换(NV12 to RGB888)和resize
/// @return 
MEDIA_BUFFER CamTele::getRgbMB_720p(int target_width, int target_height, bool block) {
    int rga_vi_width = 1280;
    int rga_vi_height = 720;

    im_rect srect;
    im_rect drect;
    im_rect prect;
    rga_buffer_t pat;
    rga_buffer_t src;
    rga_buffer_t dst;
    MEDIA_BUFFER src_mb = NULL;
    MEDIA_BUFFER dst_mb = NULL;
    int ret = 0;

	memset(&src, 0, sizeof(src));
	memset(&dst, 0, sizeof(dst));
    memset(&pat, 0, sizeof(pat));
    memset(&srect, 0, sizeof(srect));
	memset(&drect, 0, sizeof(drect));
    memset(&prect, 0, sizeof(prect));

    if (block == false) 
        src_mb = getNv12MediaBuffer();
    else
        src_mb = getNewestNv12MediaBuffer();
    
    if (!src_mb) {
        log_e("ERROR: getNv12MediaBuffer get null buffer!");
        return NULL;
    }

    src = wrapbuffer_fd(RK_MPI_MB_GetFD(src_mb), rga_vi_width, rga_vi_height, RK_FORMAT_YCbCr_420_SP);

    MB_IMAGE_INFO_S stImageInfo = {static_cast<RK_U32>(target_width), static_cast<RK_U32>(target_height), 
                                   static_cast<RK_U32>(target_width), static_cast<RK_U32>(target_height), IMAGE_TYPE_RGB888};
    dst_mb = RK_MPI_MB_CreateImageBuffer(&stImageInfo, RK_TRUE, 0);
    if (!dst_mb) {
        log_e("ERROR: RK_MPI_MB_CreateImageBuffer get null buffer!");
        rkfree(src_mb);
        return NULL;
    }
    dst = wrapbuffer_fd(RK_MPI_MB_GetFD(dst_mb), target_width, target_height, RK_FORMAT_RGB_888);

    IM_STATUS STATUS = improcess(src, dst, pat, srect, drect, prect, IM_SYNC);
    if (STATUS != IM_STATUS_SUCCESS) {
        log_e("improcess failed: %s", imStrError(STATUS));
        rkfree(dst_mb);
        rkfree(src_mb);
        return NULL;
    }

    rkfree(src_mb);

    return dst_mb;
}

void CamTele::getMediaBufferBypass() {
    int ret = 0;
    MEDIA_BUFFER mb = NULL;

    while (b_get_media_buffer_bypass) {
        mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, VI_CHANNEL_RKISPP_M_BYPASS, 100);
        if (!mb) {
            // log_i("RK_MPI_SYS_GetMediaBuffer get null buffer");
            continue;
        }
        pthread_mutex_lock(&nv12_mb_bypass_mutex);
        rkfree(nv12_mb_bypass);
        nv12_mb_bypass = RK_MPI_MB_Copy(mb, RK_TRUE);
        rkfree(mb);
        pthread_cond_broadcast(&nv12_mb_bypass_cond);
        pthread_mutex_unlock(&nv12_mb_bypass_mutex);
    }	
    rkfree(nv12_mb_bypass);
    RK_MPI_SYS_StopGetMediaBuffer(RK_ID_VI, VI_CHANNEL_RKISPP_M_BYPASS);
}

/*
获取摄像头Bypass节点NV12图像数据
*/
MEDIA_BUFFER CamTele::getNv12MediaBufferByPass() {
    MEDIA_BUFFER buffer = NULL;

    if (b_get_media_buffer_bypass) {
        pthread_mutex_lock(&nv12_mb_bypass_mutex);
        buffer = RK_MPI_MB_Copy(nv12_mb_bypass, RK_TRUE);
        pthread_mutex_unlock(&nv12_mb_bypass_mutex);
    }

    return buffer;
}

/*
    buffer需要在调用后释放，会等待当前曝光的图到达
*/
MEDIA_BUFFER CamTele::getNewestNv12MediaBufferByPass() {
    MEDIA_BUFFER buffer = NULL;
    time_checker timer;

    if (b_get_media_buffer_bypass) {
        pthread_mutex_lock(&nv12_mb_bypass_mutex);
        pthread_cond_wait(&nv12_mb_bypass_cond, &nv12_mb_bypass_mutex);
        buffer = RK_MPI_MB_Copy(nv12_mb_bypass, RK_TRUE);
        pthread_mutex_unlock(&nv12_mb_bypass_mutex);
    }

    return buffer;
}

MEDIA_BUFFER CamTele::getRgbMB_4k(int target_width, int target_height, bool block) {
    int rga_vi_width = 3840;
    int rga_vi_height = 2160;

    im_rect srect;
    im_rect drect;
    im_rect prect;
    rga_buffer_t pat;
    rga_buffer_t src;
    rga_buffer_t dst;
    MEDIA_BUFFER src_mb = NULL;
    MEDIA_BUFFER dst_mb = NULL;
    int ret = 0;

	memset(&src, 0, sizeof(src));
	memset(&dst, 0, sizeof(dst));
    memset(&pat, 0, sizeof(pat));
    memset(&srect, 0, sizeof(srect));
	memset(&drect, 0, sizeof(drect));
    memset(&prect, 0, sizeof(prect));

    if (block == false)
        src_mb = getNv12MediaBufferByPass(); 
    else 
        src_mb = getNewestNv12MediaBufferByPass(); 

    if (!src_mb) {
        // log_e("ERROR: getNv12MediaBuffer get null buffer!");
        return NULL;
    }

    src = wrapbuffer_fd(RK_MPI_MB_GetFD(src_mb), rga_vi_width, rga_vi_height, RK_FORMAT_YCbCr_420_SP);

    MB_IMAGE_INFO_S stImageInfo = {static_cast<RK_U32>(target_width), static_cast<RK_U32>(target_height), 
                                   static_cast<RK_U32>(target_width), static_cast<RK_U32>(target_height), IMAGE_TYPE_RGB888};
    dst_mb = RK_MPI_MB_CreateImageBuffer(&stImageInfo, RK_TRUE, 0);
    if (!dst_mb) {
        log_e("ERROR: RK_MPI_MB_CreateImageBuffer get null buffer!");
        rkfree(src_mb);
        return NULL;
    }
    dst = wrapbuffer_fd(RK_MPI_MB_GetFD(dst_mb), target_width, target_height, RK_FORMAT_RGB_888);

    IM_STATUS STATUS = improcess(src, dst, pat, srect, drect, prect, IM_SYNC);
    if (STATUS != IM_STATUS_SUCCESS) {
        log_e("improcess failed: %s", imStrError(STATUS));
        rkfree(dst_mb);
        rkfree(src_mb);
        return NULL;
    }

    rkfree(src_mb);

    return dst_mb;
}

/*


这里只能用c的线程锁，用c++的锁容易出现闪屏问题
*/
void CamTele::jpgPreviewOutCb(MEDIA_BUFFER mb) {
    static uint8_t* mb_ptr = NULL;

    pthread_mutex_lock(&jpg_mutex);

    xfree(mb_ptr);

    jpg_size = RK_MPI_MB_GetSize(mb);
    mb_ptr = (uint8_t*)malloc(jpg_size);
    memcpy(mb_ptr, (uint8_t*)RK_MPI_MB_GetPtr(mb), jpg_size);

    jpg_ptr = mb_ptr;

    rkfree(mb);

    jpg_frame_id++;

    pthread_cond_broadcast(&jpg_cond);
    pthread_mutex_unlock(&jpg_mutex);
}

int saveJpg(const string& jpg_path, const MEDIA_BUFFER &mb) {
    std::ofstream file(jpg_path, std::ios::binary);

    if (file.is_open()) {
        const char* buffer = (const char*)RK_MPI_MB_GetPtr(mb);
        size_t size = RK_MPI_MB_GetSize(mb);
        file.write(buffer, size);
        file.close();
        system("sync");
    }

    return 0;
}

void CamTele::jpgPhotoOutCb(MEDIA_BUFFER mb) {
    // if (getPhotoState() == CAM_TELE_PHOTO_SINGEL) {
    //     saveJpg(photo_path, mb);
    //     setPhotoState(CAM_TELE_PHOTO_IDLE);
    //     wsCommonResponse(CMD_CAMERA_TELE_PHOTOGRAPH, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, 0);
    //     wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_PHOTO, STATE_IDLE);
    //     m_album.album_picture_.updatePicture(photo_path);
    // }

    // if (getPhotoState() == CAM_TELE_PHOTO_BURST) {
    //     string jpg_path = burst_photo_path + "/" + to_string(burst_photo_id) + ".jpg";
    //     saveJpg(jpg_path, mb);
    //     burst_photo_id++;
    //     log_i("burst_photo_id = %d, count=%d", burst_photo_id, burst_photo_count);
    //     wsResNotifyBurstProgress(CMD_NOTIFY_TELE_BURST_PROGRESS, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, burst_photo_count, burst_photo_id);
       
    //     if (burst_photo_id == burst_photo_count) {
    //         setPhotoState(CAM_TELE_PHOTO_IDLE);
    //         wsCommonResponse(CMD_CAMERA_TELE_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, 0);
    //         wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_BURST, STATE_IDLE);
    //         m_album.album_burst_.updateBurst(burst_photo_path);
    //     }
    // }

    rkfree(mb);
}

//摄像头初始化
int CamTele::initCamera(bool binning) {
    //设置摄像头输出格式
    system("echo 1 > /sys/devices/platform/rkcif_mipi_lvds/compact_test");
    if (binning) 
        system("media-ctl -d /dev/media0 --set-v4l2 '\"m00_f_imx415 1-001a\":0[fmt:SGBRG12_1X12/1920x1080]'");
    else
        system("media-ctl -d /dev/media0 --set-v4l2 '\"m00_f_imx415 1-001a\":0[fmt:SGBRG12_1X12/3840x2160]'");

    // //设置默认打开红外滤光
    system("v4l2-ctl -d /dev/v4l-subdev5 --set-ctrl \"band_stop_filter=0\"");

    //初始化ISP、启动ISP、设置ISP参数
    rk_aiq_working_mode_t hdr_mode = RK_AIQ_WORKING_MODE_NORMAL;
    cam_tele_attr_t local_st_cam_attr = st_cam_attr;
    if (binning)
        local_st_cam_attr = st_cam_attr_binning;
    SAMPLE_COMM_ISP_Init(local_st_cam_attr.cam_id, hdr_mode, RK_FALSE,
                         local_st_cam_attr.pc_iqfile_dir);
    SAMPLE_COMM_ISP_Run(local_st_cam_attr.cam_id);
    SAMPLE_COMM_ISP_SET_mirror(local_st_cam_attr.cam_id, local_st_cam_attr.flip);
    SAMPLE_COMM_ISP_SET_BayerNr_Enable(local_st_cam_attr.cam_id, false);
    SAMPLE_COMM_ISP_SET_Dehaze_Enable(local_st_cam_attr.cam_id, false);

    return 0;
}

//输入通道初始化
int CamTele::initViChannel(bool binning) {
    RK_S32 ret = 0;
    log_i("init vi channel");

    cam_tele_attr_t local_st_cam_attr = st_cam_attr;
    if (binning)
        local_st_cam_attr = st_cam_attr_binning;
    
    //摄像头输入通道
    VI_CHN_ATTR_S vi_chn_attr;

    memset(&vi_chn_attr, 0, sizeof(vi_chn_attr));
    vi_chn_attr.pcVideoNode = local_st_cam_attr.pc_video_node;
    vi_chn_attr.u32BufCnt = local_st_cam_attr.vi_buf_count;
    vi_chn_attr.u32Width = WIDTH_1280;
    vi_chn_attr.u32Height = HEIGHT_720;
    vi_chn_attr.enPixFmt = local_st_cam_attr.pix_fmt;
    vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
    // vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;
    ret = RK_MPI_VI_SetChnAttr(local_st_cam_attr.cam_id, VI_CHANNEL_RKISPP_SCALE0, &vi_chn_attr);
    ret |= RK_MPI_VI_EnableChn(local_st_cam_attr.cam_id, VI_CHANNEL_RKISPP_SCALE0);
    if (ret) {
        log_i("Create Vi[%d] failed! ret=%d", VI_CHANNEL_RKISPP_SCALE0, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    memset(&vi_chn_attr, 0, sizeof(vi_chn_attr));
    vi_chn_attr.pcVideoNode = "rkispp_m_bypass";
    vi_chn_attr.u32BufCnt = local_st_cam_attr.vi_buf_count;
    vi_chn_attr.u32Width = local_st_cam_attr.src_width;
    vi_chn_attr.u32Height = local_st_cam_attr.src_height;
    vi_chn_attr.enPixFmt = local_st_cam_attr.pix_fmt;
    vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
    // vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;
    ret = RK_MPI_VI_SetChnAttr(local_st_cam_attr.cam_id, VI_CHANNEL_RKISPP_M_BYPASS, &vi_chn_attr);
    ret |= RK_MPI_VI_EnableChn(local_st_cam_attr.cam_id, VI_CHANNEL_RKISPP_M_BYPASS);
    if (ret) {
        log_i("Create Vi[%d] failed! ret=%d", VI_CHANNEL_RKISPP_M_BYPASS, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    return 0;
}

//编码输出通道初始化
int CamTele::initVencChannel(bool binning) {
    RK_S32 ret = 0;

    log_i("init venc channel");
    cam_tele_attr_t local_st_cam_attr = st_cam_attr;
    if (binning)
        local_st_cam_attr = st_cam_attr_binning;

    //编码通道
    VENC_CHN_ATTR_S venc_chn_attr;

    //jpeg编码通道，用于图传
    memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
    venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_JPEG;
    venc_chn_attr.stVencAttr.imageType = local_st_cam_attr.pix_fmt;
    venc_chn_attr.stVencAttr.u32PicWidth = WIDTH_1280;
    venc_chn_attr.stVencAttr.u32PicHeight = HEIGHT_720;
    venc_chn_attr.stVencAttr.u32VirWidth = WIDTH_1280;
    venc_chn_attr.stVencAttr.u32VirHeight = HEIGHT_720;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomWidth = WIDTH_1280;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomHeight = HEIGHT_720;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomVirWidth = WIDTH_1280;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomVirHeight = HEIGHT_720;
    venc_chn_attr.stVencAttr.enRotation = enRotation;
    ret = RK_MPI_VENC_CreateChn(VENC_CHANNEL_JPEG_PREVIEW, &venc_chn_attr);
    if (ret) {
        log_i("Create Venc[%d] failed! ret=%d", VENC_CHANNEL_JPEG_PREVIEW, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    //jpeg编码通道，用于拍照
    memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
    venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_JPEG;
    venc_chn_attr.stVencAttr.imageType = local_st_cam_attr.pix_fmt;
    venc_chn_attr.stVencAttr.u32PicWidth = local_st_cam_attr.src_width;
    venc_chn_attr.stVencAttr.u32PicHeight = local_st_cam_attr.src_height;
    venc_chn_attr.stVencAttr.u32VirWidth = local_st_cam_attr.src_width;
    venc_chn_attr.stVencAttr.u32VirHeight = local_st_cam_attr.src_height;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomWidth = local_st_cam_attr.src_width;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomHeight = local_st_cam_attr.src_height;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomVirWidth = local_st_cam_attr.src_width;
    venc_chn_attr.stVencAttr.stAttrJpege.u32ZoomVirHeight = local_st_cam_attr.src_height;
    venc_chn_attr.stVencAttr.enRotation = enRotation;
    ret = RK_MPI_VENC_CreateChn(VENC_CHANNEL_JPEG_PHOTO, &venc_chn_attr);
    if (ret) {
        log_i("Create Venc[%d] failed! ret=%d", VENC_CHANNEL_JPEG_PREVIEW, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    //h264编码通道, 用于录像
    memset(&venc_chn_attr, 0, sizeof(venc_chn_attr));
    venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
    venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264VBR;
    venc_chn_attr.stRcAttr.stH264Vbr.u32Gop = 30;
    venc_chn_attr.stRcAttr.stH264Vbr.u32MaxBitRate = H264_RECORD_BITRATE;
    // frame rate: in 30/1, out 30/1.
    venc_chn_attr.stRcAttr.stH264Vbr.fr32DstFrameRateDen = 1;
    venc_chn_attr.stRcAttr.stH264Vbr.fr32DstFrameRateNum = H264_RECORD_FPS;
    venc_chn_attr.stRcAttr.stH264Vbr.u32SrcFrameRateDen = 1;
    venc_chn_attr.stRcAttr.stH264Vbr.u32SrcFrameRateNum = H264_RECORD_FPS;
    venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
    venc_chn_attr.stVencAttr.u32PicWidth = local_st_cam_attr.src_width;
    venc_chn_attr.stVencAttr.u32PicHeight = local_st_cam_attr.src_height;
    venc_chn_attr.stVencAttr.u32VirWidth = local_st_cam_attr.src_width;
    venc_chn_attr.stVencAttr.u32VirHeight = local_st_cam_attr.src_height;
    venc_chn_attr.stVencAttr.u32Profile = 100;
    ret = RK_MPI_VENC_CreateChn(VENC_CHANNEL_H264_RECORD, &venc_chn_attr);
    if (ret) {
        log_i("Create Venc[%d] failed! ret=%d", VENC_CHANNEL_H264_RECORD, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    return 0;
}

int CamTele::closeCamera() {
    int ret;
    log_i("cam tele close camera");
    unique_lock<std::mutex> lock(camera_mutex);

    if (record_state == CAM_RECORDING) {  // 关摄像头时，停止录像
        stopRecord();
    }
    
    if ((ret = isCameraOpen()) < 0) {
        return 0;
    }

    b_get_media_buffer = false;
    b_get_media_buffer_bypass = false;
    
    if (get_media_buffer_t.valid())
        get_media_buffer_t.wait();

    if (get_media_buffer_bypass_t.valid())
        get_media_buffer_bypass_t.wait();
    
    // log_i("cam tele close camera");

    cam_tele_attr_t local_st_cam_attr = st_cam_attr;

    //输入输出解绑
    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32ChnId = VI_CHANNEL_RKISPP_SCALE0;
    stDestChn.enModId = RK_ID_VENC;
    stDestChn.s32ChnId = VENC_CHANNEL_JPEG_PREVIEW;
    RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);

    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32ChnId = VI_CHANNEL_RKISPP_M_BYPASS;
    stDestChn.enModId = RK_ID_VENC;
    stDestChn.s32ChnId = VENC_CHANNEL_JPEG_PHOTO;
    RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);

    RK_MPI_VENC_DestroyChn(VENC_CHANNEL_JPEG_PREVIEW);
    RK_MPI_VENC_DestroyChn(VENC_CHANNEL_JPEG_PHOTO);
    RK_MPI_VENC_DestroyChn(VENC_CHANNEL_H264_RECORD);

    RK_MPI_VI_DisableChn(st_cam_attr.cam_id, VI_CHANNEL_RKISPP_SCALE0);
    RK_MPI_VI_DisableChn(st_cam_attr.cam_id, VI_CHANNEL_RKISPP_M_BYPASS);

    if (st_cam_attr.pc_iqfile_dir) {
        SAMPLE_COMM_ISP_Stop(st_cam_attr.cam_id);
    }

    setCameraState(false);

    usleep(400 * 1000);

    return 0;
}

int CamTele::closeCamera(WsPacket& ws_packet) {
    wsCommonResponse(CMD_CAMERA_TELE_CLOSE_CAMERA, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, 0);

    return 0;
}

int CamTele::initRKmedia() {
    int ret = 0;
    ret = RK_MPI_SYS_Init();
    if (ret) {
        log_i("Sys Init failed! ret=%d", ret);
    }

    LOG_LEVEL_CONF_S log_conf;
    log_conf.enModId = RK_ID_SYS;
    log_conf.s32Level = 0;
    RK_MPI_LOG_SetLevelConf(&log_conf);

    return 0;
}

int CamTele::openCamera(bool binning) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    
    if ((ret = isCameraClose()) < 0) {
        return 0;
    }
    log_i("cam tele open camera");

    if (state_manager->astro_.getCaptureDarkState() != STATE_IDLE || state_manager->astro_.getCaptureLightState() != STATE_IDLE) {
        return 0;
    }

    initCamera(false);

    ret = initViChannel(false);
    if (ret < 0)
        return ret;

    ret = initVencChannel(false);
    if (ret < 0)
        return ret;

    //注册jpeg编码回调函数
    MPP_CHN_S stEncChn;
    stEncChn.enModId = RK_ID_VENC;
    stEncChn.s32ChnId = VENC_CHANNEL_JPEG_PREVIEW;
    ret = RK_MPI_SYS_RegisterOutCb(&stEncChn, jpgPreviewOutCb);
    if (ret) {
        log_i("Create Venc[%d] failed! ret=%d", VENC_CHANNEL_JPEG_PREVIEW, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    // stEncChn.s32ChnId = VENC_CHANNEL_JPEG_PHOTO;
    // ret = RK_MPI_SYS_RegisterOutCb(&stEncChn, jpgPhotoOutCb);
    // if (ret) {
    //     log_i("Create Venc[%d] failed! ret=%d", VENC_CHANNEL_JPEG_PHOTO, ret);
    //     return -CODE_CAMERA_TELE_OPEN_FAILED;
    // }

    //设置jpeg编码参数
    VENC_JPEG_PARAM_S st_jpeg_param;
    st_jpeg_param.u32Qfactor = jpeg_defalut_quality;
    RK_MPI_VENC_SetJpegParam(VENC_CHANNEL_JPEG_PHOTO, &st_jpeg_param);
    st_jpeg_param.u32Qfactor = jpeg_preview_quality;
    RK_MPI_VENC_SetJpegParam(VENC_CHANNEL_JPEG_PREVIEW, &st_jpeg_param);

    // The encoder defaults to continuously receiving frames from the previous
    // stage. Before performing the bind operation, set s32RecvPicNum to 0 to
    // make the encoding enter the pause state.
    st_recv_param.s32RecvPicNum = 0;
    RK_MPI_VENC_StartRecvFrame(VENC_CHANNEL_JPEG_PHOTO, &st_recv_param);

    //绑定输入输出
    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32ChnId = VI_CHANNEL_RKISPP_SCALE0;
    stDestChn.enModId = RK_ID_VENC;

    stDestChn.s32ChnId = VENC_CHANNEL_JPEG_PREVIEW;
    ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
    if (ret) {
        log_i("Bind VI[%d] to VENC[%d] error! ret=%d", VI_CHANNEL_RKISPP_SCALE0, VENC_CHANNEL_JPEG_PREVIEW, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    stSrcChn.s32ChnId = VI_CHANNEL_RKISPP_M_BYPASS;
    stDestChn.s32ChnId = VENC_CHANNEL_JPEG_PHOTO;
    ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
    if (ret) {
        log_i("Bind VI[%d] to VENC[%d] error! ret=%d", VI_CHANNEL_RKISPP_SCALE0, VENC_CHANNEL_JPEG_PREVIEW, ret);
        return -CODE_CAMERA_TELE_OPEN_FAILED;
    }

    
    setCameraState(true);
    b_binning = binning;
    b_auto_exposure = true;
    b_auto_gain = true;

    b_get_media_buffer = false;
    b_get_media_buffer_bypass = false;

    if (get_media_buffer_t.valid())
        get_media_buffer_t.wait();

    if (get_media_buffer_bypass_t.valid())
        get_media_buffer_bypass_t.wait();

    b_get_media_buffer = true;
    get_media_buffer_t = std::async(std::launch::async, [&]() {
        getMediaBuffer();
    });

    b_get_media_buffer_bypass = true;
    get_media_buffer_bypass_t = std::async(std::launch::async, [&]() {
        getMediaBufferBypass();
    });

    usleep(500 * 1000);

    return 0;
}

int CamTele::openCamera(WsPacket& ws_packet) {
    int ret;
    ReqOpenCamera req_open_camera;
    string serialized_data = ws_packet.data();

    if (!req_open_camera.ParseFromString(serialized_data)) {
        ret =  -WS_PARSE_PROTOBUF_ERROR;
        wsCommonResponse(CMD_CAMERA_TELE_OPEN_CAMERA, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
    }

    log_i("binning = %d", req_open_camera.binning());  
    ret = openCamera(req_open_camera.binning());
    
    wsCommonResponse(CMD_CAMERA_TELE_OPEN_CAMERA, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return 0;
}

bool CamTele::getCameraState() {
    return b_cam_open;
}

void CamTele::setCameraState(bool state) {
    log_i("set cam tele state to %d", state);
    b_cam_open = state;
}

bool CamTele::getBinningState() {
    return b_binning;
}

void CamTele::setBinningState(bool state) {
    b_binning = state;
}

void CamTele::setPhotoState(CamTelePhotoState state) {
    log_i("set photo state = %d", state);
    photo_state = state;
}

CamTelePhotoState CamTele::getPhotoState() {
    return photo_state;
}

void CamTele::setRecordState(CamTeleRecordState state) {
    log_i("set record state to %d", state);
    record_state = state;
}

CamTeleRecordState CamTele::getRecordState() {
    return record_state;
}

void CamTele::setTimelapseState(CamTeleTimelapseState state) {
    log_i("set timelapse state to %d", state);
    timelapse_state = state;
}

CamTeleTimelapseState CamTele::getTimelapseState() {
    // log_i("getTimelapseState = %d", timelapse_state);
    return timelapse_state;
}

int getCamTeleTimelapState() {
    return timelapse_state;
}

void CamTele::setPanoramaState(CamTelePanoramaState state) {
    log_i("set panoramaState state to %d", state);
    panorama_state = state;
}

CamTelePanoramaState CamTele::getPanoramaState() {
    return panorama_state;
}

int CamTele::photograph(std::string file_name) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);
    
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    if (getPhotoState() != CAM_TELE_PHOTO_IDLE) {
        log_e("cam tele is photographing");
        return 0;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        return -WS_SDCARD_NOT_EXIST;
    }

    try {
        if (!fs::exists(album_path + photograph_path))
            fs::create_directory(album_path + photograph_path);
    } catch (const fs::filesystem_error& ex) {
        log_e("Error creating directories: %s", ex.what());
        return -WS_SDCARD_WRITE_ERROR;
    }

    photo_path = file_name;
    log_i("photograph, file name is %s", photo_path.c_str());
    setPhotoState(CAM_TELE_PHOTO_SINGEL);

    wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_PHOTO, STATE_RUNNING);

    MEDIA_BUFFER img_buff = getNv12MediaBufferByPass();
    const uint8_t* nv12_data = (const uint8_t*)RK_MPI_MB_GetPtr(img_buff);
    ret = saveNV12ToJpg(nv12_data, 3840, 2160, photo_path);
    rkfree(img_buff);
    setPhotoState(CAM_TELE_PHOTO_IDLE);
    wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_PHOTO, STATE_IDLE);

    if (ret == false) {
        return -WS_SDCARD_WRITE_ERROR;
    } else {
        m_album.album_picture_.updatePicture(photo_path);
        return 0; 
    }

    // if (ratio > 1) {
    //     MEDIA_BUFFER img_buff = getNv12MediaBufferByPass();
    //     const uint8_t* nv12_data = (const uint8_t*)RK_MPI_MB_GetPtr(img_buff);
    //     if (b_binning)
    //         nv12ToJpgWithCropAndResize(nv12_data, 1920, 1080, photo_path, x, y, ratio);
    //     else
    //         nv12ToJpgWithCropAndResize(nv12_data, 3840, 2160, photo_path, 2 * x, 2 * y, ratio);

    //     rkfree(img_buff);
    //     setPhotoState(CAM_TELE_PHOTO_IDLE);
    //     return 0;
    // } 

    // st_recv_param.s32RecvPicNum = 1;
    // RK_MPI_VENC_StartRecvFrame(1, &st_recv_param);
}

int CamTele::photograph(WsPacket& ws_packet) {
    int ret; 
    ReqPhoto req_photo;
    string serialized_data = ws_packet.data();

    if (!req_photo.ParseFromString(serialized_data)) {
        ret =  -WS_PARSE_PROTOBUF_ERROR;
        wsCommonResponse(CMD_CAMERA_TELE_PHOTOGRAPH, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    // log_i("x = %d, y = %d, ratio = %d", req_photo.x(), req_photo.y(), req_photo.ratio());
    string file_name = album_path + photograph_path + photo_name_prefix + getTimeStamp() + ".jpg";
    ret = photograph(file_name);

    wsCommonResponse(CMD_CAMERA_TELE_PHOTOGRAPH, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return ret;
}

int CamTele::burstPhotograph(int img_count) {
    int ret;
    unique_lock<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        wsCommonResponse(CMD_CAMERA_TELE_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    if (getPhotoState() != CAM_TELE_PHOTO_IDLE) {
        log_e("cam tele is photographing");
        wsCommonResponse(CMD_CAMERA_TELE_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, WS_OK);
        return 0;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        wsCommonResponse(CMD_CAMERA_TELE_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        return -WS_SDCARD_NOT_EXIST;
    }

    try {
        fs::create_directory(album_path + burst_path);
    } catch (const fs::filesystem_error& ex) {
        log_e("Error creating directories: %s", ex.what());
        wsCommonResponse(CMD_CAMERA_TELE_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
        return -WS_SDCARD_WRITE_ERROR;
    }

    string path = getTimeStamp();
    burst_photo_path = album_path + burst_path + burst_photo_path_prefix + path;
    log_i("burst photo path is %s", burst_photo_path.c_str());
    string cmd = "mkdir " + burst_photo_path;
    system(cmd.c_str());

    setPhotoState(CAM_TELE_PHOTO_BURST);
    burst_photo_count = img_count;
    burst_photo_id = 0;

    lock.unlock();  //保证连拍过程中可以执行停止命令

    wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_BURST, STATE_RUNNING);

    while (getPhotoState() == CAM_TELE_PHOTO_BURST) {
        string jpg_path = burst_photo_path + "/" + to_string(burst_photo_id) + ".jpg";
        MEDIA_BUFFER img_buff = getNv12MediaBufferByPass();
        const uint8_t* nv12_data = (const uint8_t*)RK_MPI_MB_GetPtr(img_buff);
        ret = saveNV12ToJpg(nv12_data, 3840, 2160, jpg_path);
        rkfree(img_buff);

        if (ret == false) {
            ret = -WS_SDCARD_WRITE_ERROR;
            break;
        } else {
            ret = 0;
        }

        burst_photo_id++;
        log_i("burst_photo_id = %d, count=%d", burst_photo_id, burst_photo_count);
        wsResNotifyBurstProgress(CMD_NOTIFY_TELE_BURST_PROGRESS, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, burst_photo_count, burst_photo_id);
        
        if (burst_photo_id == burst_photo_count) {
            break;
        }

        if (burst_photo_interval > 0)
            sleep(burst_photo_interval);
    }

    if (burst_photo_id > 0)
        m_album.album_burst_.updateBurst(burst_photo_path);

    if (getPhotoState() == CAM_TELE_PHOTO_IDLE) {
        wsCommonResponse(CMD_CAMERA_TELE_STOP_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, WS_OK);
    } else {
        wsCommonResponse(CMD_CAMERA_TELE_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
    }

    setPhotoState(CAM_TELE_PHOTO_IDLE);
    wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_BURST, STATE_IDLE);

    return 0;
}

int CamTele::stopBurstPhoto() {
    lock_guard<mutex> lock(camera_mutex);
    setPhotoState(CAM_TELE_PHOTO_IDLE);

    return 0;
}

int CamTele::burstPhotograph(WsPacket& ws_packet) {
    int ret;
    ReqBurstPhoto req_burst_photo;
    string serialized_data = ws_packet.data();

    if (!req_burst_photo.ParseFromString(serialized_data)) {
        ret =  -WS_PARSE_PROTOBUF_ERROR;
        wsCommonResponse(CMD_CAMERA_TELE_BURST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }
    ret = burstPhotograph(burst_photo_count);

    return ret;
}

int CamTele::stopBurstPhoto(WsPacket& ws_packet) {
    int ret = stopBurstPhoto();

    return ret;
}


typedef struct {
    RK_U32 u32ModeIdx;
    RK_U32 u32ChnIdx;
} MuxerHandle;

void muxer_event_cb(RK_VOID* pHandle, RK_VOID* pstEvent) {
    RK_S32 s32ModeIdx = -1;
    RK_S32 s32ChnIdx = -1;

    log_i("### %s: Handle:%p, event:%p", __func__, pHandle, pstEvent);
    if (pHandle) {
        MuxerHandle* pstMuxerHandle = (MuxerHandle*)pHandle;
        s32ModeIdx = (RK_S32)pstMuxerHandle->u32ModeIdx;
        s32ChnIdx = (RK_S32)pstMuxerHandle->u32ChnIdx;
    }
    if (pstEvent) {
        MUXER_EVENT_INFO_S* pstMuxerEvent = (MUXER_EVENT_INFO_S*)pstEvent;
        log_i("@@@ %s: ModeID:%d, ChnID:%d, EventType:%d, filename:%s, "
            "value:%d",
            __func__, s32ModeIdx, s32ChnIdx, pstMuxerEvent->enEvent,
            pstMuxerEvent->unEventInfo.stFileInfo.asFileName,
            (int)pstMuxerEvent->unEventInfo.stFileInfo.u32Duration);
    }
}

int CamTele::startRecord(bool notify) {
    int ret = 0;
    unique_lock<std::mutex> lock(camera_mutex);
    
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        return -WS_SDCARD_NOT_EXIST;
    }

    try {
        if (!fs::exists(album_path + video_path))
            fs::create_directory(album_path + video_path);
    } catch (const fs::filesystem_error& ex) {
        log_e("Error creating directories: %s", ex.what());
        return -WS_SDCARD_WRITE_ERROR;
    }

    if (getRecordState() != CAM_RECORD_IDLE) {
        log_i("cam tele is recording");
        return 0;
    }
    log_i("start record");
    setRecordState(CAM_RECORDING);

    lock.unlock();

    string file_name = getTimeStamp();
    m_video_path = album_path + video_path + record_name_prefix + file_name + ".mp4";
    log_i("video path = %s", m_video_path.c_str());

    MUXER_CHN_ATTR_S stMuxerAttr;
    memset(&stMuxerAttr, 0, sizeof(stMuxerAttr));
    stMuxerAttr.enMode = MUXER_MODE_SINGLE;
    stMuxerAttr.enType = MUXER_TYPE_MP4;
    stMuxerAttr.pcOutputFile = const_cast<RK_CHAR*>(m_video_path.c_str());

    stMuxerAttr.stVideoStreamParam.enCodecType = RK_CODEC_TYPE_H264;
    stMuxerAttr.stVideoStreamParam.enImageType = IMAGE_TYPE_NV12;
    stMuxerAttr.stVideoStreamParam.u16Fps = H264_RECORD_FPS;
    stMuxerAttr.stVideoStreamParam.u16Level = 100;    // for h264
    stMuxerAttr.stVideoStreamParam.u16Profile = 100;  // for h264
    stMuxerAttr.stVideoStreamParam.u32BitRate = H264_RECORD_BITRATE;
    stMuxerAttr.stVideoStreamParam.u32Width = 3840;
    stMuxerAttr.stVideoStreamParam.u32Height = 2160;

    ret = RK_MPI_MUXER_EnableChn(0, &stMuxerAttr);
    if (ret) {
        log_e("Create MUXER[0] failed! ret=%d", ret);
        return -CODE_CAMERA_TELE_START_RECORD_FAILED;
    }

    stSrcChn.enModId = RK_ID_MUXER;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;

    MuxerHandle stMuxerHandle;
    stMuxerHandle.u32ChnIdx = 0;
    stMuxerHandle.u32ModeIdx = RK_ID_MUXER;

    ret = RK_MPI_SYS_RegisterEventCb(&stSrcChn, &stMuxerHandle,
                                        muxer_event_cb);
    if (ret) {
        log_e("Register event callback failed! ret=%d", ret);
        return -CODE_CAMERA_TELE_START_RECORD_FAILED;
    }

    log_i("### Start muxer stream...");
    ret = RK_MPI_MUXER_StreamStart(0);
    if (ret) {
        log_e("Muxer start stream failed! ret=%d", ret);
        return -CODE_CAMERA_TELE_START_RECORD_FAILED;
    }

    // Bind VENC[2] to MUXER[0]:VIDEO
    stDestChn.enModId = RK_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VENC_CHANNEL_H264_RECORD;
    stMuxerChn.enModId = RK_ID_MUXER;
    stMuxerChn.enChnType = MUXER_CHN_TYPE_VIDEO;
    stMuxerChn.s32ChnId = 0;
    ret = RK_MPI_MUXER_Bind(&stDestChn, &stMuxerChn);
    if (ret) {
        log_e("ERROR: Bind VENC[2] and MUXER[0]:VIDEO error! ret=%d",
                ret);
        return -CODE_CAMERA_TELE_START_RECORD_FAILED;
    }

    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = VI_CHANNEL_RKISPP_M_BYPASS;
    stDestChn.enModId = RK_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VENC_CHANNEL_H264_RECORD;
    ret = RK_MPI_SYS_Bind(&stSrcChn, &stDestChn);
    if (ret) {
        log_e("ERROR: Bind VI[1] and VENC[2] error! ret=%d", ret);
        return -CODE_CAMERA_TELE_START_RECORD_FAILED;
    }

    // 开启定时器
    record_timer_manager.startTimer([&](int elapsed_time) {
            log_i("record time = %d", elapsed_time);
            record_time = elapsed_time;
            wsResNotifyRecordTime(CMD_NOTIFY_TELE_RECORD_TIME, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, elapsed_time);

            if (elapsed_time % 120 == 0 && state_manager->system_.getSdCardAvailableAndTotal().available_size < 1) {
                stopRecord(notify);
            }
    }, 1);

    if (notify) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_RECORD, STATE_RUNNING);
    }
    //生成视频缩略图
    
    string thumbnail_path = album_path + video_path + video_thumbnail_path + record_name_prefix + file_name + ".jpg";
    MEDIA_BUFFER img_buff = getNv12MediaBuffer();
    const uint8_t* nv12_data = (const uint8_t*)RK_MPI_MB_GetPtr(img_buff);
    saveNV12ToJpgWithResize(nv12_data, WIDTH_1280, HEIGHT_720, thumbnail_path, album_thumbnail_width, album_thumbnail_height);
    rkfree(img_buff);
    
    return 0;
}

int CamTele::startRecord(WsPacket& ws_packet) {
    int ret = startRecord();
    wsCommonResponse(CMD_CAMERA_TELE_START_RECORD, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return ret;
}

int CamTele::stopRecord(bool notify) {
    int ret = 0;
    lock_guard<mutex> lock(camera_mutex);
    log_i("stopRecord");
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    if (getRecordState() == CAM_RECORD_IDLE) {
        log_e("cam tele Recording has stopped");
        return ret;
    }

    ret = RK_MPI_MUXER_StreamStop(0);
    if (ret) {
        log_i("Muxer stop stream failed! ret=%d", ret);
        return -CODE_CAMERA_TELE_STOP_RECORD_FAILED;
    }

    // UnBind VENC[2] to MUXER[0]
    stDestChn.enModId = RK_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VENC_CHANNEL_H264_RECORD;
    stMuxerChn.enModId = RK_ID_MUXER;
    stMuxerChn.enChnType = MUXER_CHN_TYPE_VIDEO;
    stMuxerChn.s32ChnId = 0;
    ret = RK_MPI_MUXER_UnBind(&stDestChn, &stMuxerChn);
    if (ret) {
        log_i("ERROR: UnBind VENC[2] and MUXER[0] error! ret=%d", ret);
        return -CODE_CAMERA_TELE_STOP_RECORD_FAILED;
    }

    stSrcChn.enModId = RK_ID_VI;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = VI_CHANNEL_RKISPP_M_BYPASS;
    stDestChn.enModId = RK_ID_VENC;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = VENC_CHANNEL_H264_RECORD;
    ret = RK_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
    if (ret) {
        log_i("ERROR: UnBind VI[1] and VENC[2] error! ret=%d", ret);
        return -CODE_CAMERA_TELE_STOP_RECORD_FAILED;
    }

    // Destroy MUXER[0]
    ret = RK_MPI_MUXER_DisableChn(0);
    if (ret) {
        log_i("ERROR: Destroy MUXER[0] error! ret=%d", ret);
        return -CODE_CAMERA_TELE_STOP_RECORD_FAILED;
    }
    
    record_timer_manager.stopTimer();
    log_i("stop record timer");
    
    string cur_video_path = m_video_path;
    setRecordState(CAM_RECORD_IDLE);
    if (notify) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_RECORD, STATE_IDLE);
    }
    record_loading_count += 1;
    system("sync");
    m_album.album_video_.updateVideo(cur_video_path);
    record_loading_count -= 1;
    wsNotifyAlbumUpdate(CMD_NOTIFY_ALBUM_UPDATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, MEDIA_VIDEO);

    return 0;
}

int CamTele::stopRecord(WsPacket& ws_packet) {
    record_timer_manager.stopTimer();
    int ret = stopRecord();
    wsCommonResponse(CMD_CAMERA_TELE_STOP_RECORD, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return 0;
}

int CamTele::isCameraOpen() {
    if (!b_cam_open) {
        log_e("cam tele is closed");
        return -CODE_CAMERA_TELE_CLOSED;
    }

    return 0;
}

int CamTele::isCameraClose() {
    if (b_cam_open) {
        log_e("cam tele is opened");
        return -CODE_CAMERA_TELE_OPENED;
    }

    return 0;
}

/*----------------------------------------------ISP---------------------------------------------------*/
int CamTele::setExpMode(int mode) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set exp mode to %d", mode);
    ret = SAMPLE_COMM_ISP_SET_ExposureMode(0, mode);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : WS_OK;

    if (ret == WS_OK && mode == 0)
        b_auto_exposure = true;
    if (ret == WS_OK && mode == 1)
        b_auto_exposure = false;

    if (ret == WS_OK) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, mode, 0, 0, 75, 0);
    }

    return ret;
}

int CamTele::getExpMode(opMode_t *mode) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_ExpMode(0, mode);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}


int CamTele::setExp(double value) {
    log_i("set Exp to %f", value);

    // 如果摄像头打开了，并且是自动曝光，先设置为手动曝光
    if (b_cam_open && b_auto_exposure) {
        setExpMode(1);
        usleep(100 * 1000);             //延时低了，获取的曝光时间和设置的不一致，100ms没问题,20ms不行，暂定100ms
    }

    lock_guard<mutex> lock(camera_mutex);

    int fd;
    struct v4l2_control control;
    double ratio = 33.0 / 2250;
    int exp = value * 1000 / ratio;
    int vb = exp - 2184;
    if (vb < 58)
        vb = 58;

    if ((fd = open("/dev/v4l-subdev4", O_RDWR)) == -1) {
        log_e("Error opening cam0 dev");
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }

    control.id =  0x009e0901;
    control.value = vb;
    if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0) {
        log_e("set vb control error");
        close(fd);
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }
    
    control.id =  0x00980911;
    control.value = exp;
    if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0) {
        log_e("set exp control error");
        close(fd);
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }
    
    close(fd);

    int index = findClosestExpIndex(exp_settings, value);
    log_i("set exp index = %d", index);
    wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, 1, 0, 0, index, 0);
    
    return 0;
}

int CamTele::getExp(double *value) {
    char cmd[50] = {0};
    int ret = -1;
    int fd;
    struct v4l2_control control;

    lock_guard<mutex> lock(camera_mutex);

    if ((fd = open("/dev/v4l-subdev4", O_RDWR)) == -1) {
            log_e("Error opening cam0 dev");
            return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }

    control.id =  0x00980911;
    if (ioctl(fd, VIDIOC_G_CTRL, &control) < 0) {
        log_e("get exp control error");
        close(fd);
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }

    *value = control.value * 33.0 / 2250 / 1000;
    log_i("get exp is %f", *value);

    close(fd);

    return 0;
}

int CamTele::setGainMode(int mode) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }
    log_i("set gain mode to %d", mode);
    ret = SAMPLE_COMM_ISP_SET_GainMode(0, mode);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : WS_OK;

    return 0;
}

int CamTele::getGainMode(int *mode) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }
    ret = SAMPLE_COMM_ISP_GET_GainMode(0, mode);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}

int CamTele::setGain(int value) {
    int fd;
    struct v4l2_control control;
    int ret;

    // 如果摄像头打开了，并且是自动曝光，先设置为手动曝光
    if (b_cam_open && b_auto_exposure) {
        setExpMode(1);
        usleep(100 * 1000);             //延时低了，获取的曝光时间和设置的不一致，100ms没问题,20ms不行，暂定100ms
    }

    lock_guard<mutex> lock(camera_mutex);

    if ((fd = open("/dev/v4l-subdev4", O_RDWR)) == -1) {
        log_e("Error opening cam0 dev");
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }

    control.id =  0x009e0903;
    control.value = value;
    if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0) {
        log_e("set gain control error");
        close(fd);
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }
    
    log_i("set gain to %d", value);

    if (ret == 0) {
        int index = findClosestIntValueIndex(gain_settings, value);
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, 1, 1, 0, index, 0);
    }

    close(fd);
    
    return 0;
}

int CamTele::getGain(int *value) {
    int fd;
    struct v4l2_control control;
    lock_guard<mutex> lock(camera_mutex);

    if ((fd = open("/dev/v4l-subdev4", O_RDWR)) == -1) {
            log_e("Error opening cam0 dev");
            return -1;
    }

    control.id =  0x009e0903;
    if (ioctl(fd, VIDIOC_G_CTRL, &control) < 0) {
        log_e("get gain control error");
        close(fd);
        return -1;
    }

    *value = control.value;
    log_i("get gain is %d", control.value);
    close(fd);

    return 0;
}

int CamTele::setIrCut(int value) {
    int fd;
    struct v4l2_control control;
    lock_guard<mutex> lock(camera_mutex);

    if ((fd = open(IRCUT_DEV, O_RDWR)) == -1) {
        log_e("Error opening ircut dev");
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }

    control.id = 0x00980921;
    control.value = value;
    if (ioctl(fd, VIDIOC_S_CTRL, &control) < 0) {
        log_e("set ircut control error");
        return -CODE_CAMERA_TELE_ISP_SET_FAILED;
    }
    log_i("set ircut to %d", value);
    close(fd);

    wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 1, 8, 0, value, 0);

    return 0;
}

int CamTele::getIrCut(int *value) {
    int fd;
    struct v4l2_control control;
    lock_guard<mutex> lock(camera_mutex);
    if ((fd = open(IRCUT_DEV, O_RDWR)) == -1) {
        log_e("Error opening ircut dev");
        return -1;
    }

    control.id = 0x00980921;
    if (ioctl(fd, VIDIOC_G_CTRL, &control) < 0) {
        log_e("get ircut control error");
        return -1;
    }
    
    close(fd);
    *value = control.value;
    log_i("get ircut = %d", *value);

    return 0;
}

int CamTele::setWBMode(opMode_t mode) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set wb mode to %d", mode);
    ret = SAMPLE_COMM_ISP_SET_WBMode(0, mode);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return 0;
}

int CamTele::getWBMode(opMode_t *mode) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    
    ret = SAMPLE_COMM_ISP_GET_WBMode(0, mode);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("get wb mode %d", *mode);

    return 0;
}

int CamTele::setWBScene(rk_aiq_wb_scene_t scene) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set wb scene to %d", scene);
    ret = SAMPLE_COMM_ISP_SET_WBScene(0, scene);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return 0;
}

int CamTele::getWBScene(rk_aiq_wb_scene_t *scene) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_WBScene(0, scene);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("get wb scence %d", *scene);

    return 0;
}

int CamTele::setWBCT(int value) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set wb ct to %d", value);
    ret = SAMPLE_COMM_ISP_SET_WBCT(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}

int CamTele::getWBCT(RK_U32 *value) {
    int ret;
    lock_guard<mutex> lock(camera_mutex);

    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_WBCT(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("get wb ct %d", *value);
    return ret;
}

int CamTele::setExpMode(WsPacket& ws_packet) {
    int ret;
    ReqSetExpMode req_set_exp_mode;

    string serialized_data = ws_packet.data();
    if (!req_set_exp_mode.ParseFromString(serialized_data)) {
        log_i("parse error");
        ret = -WS_PARSE_PROTOBUF_ERROR;
        wsCommonResponse(CMD_CAMERA_TELE_SET_EXP_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
    }

    log_i("setExpMode b_auto_exposure = %d", b_auto_exposure);
    if (!b_auto_exposure) {
        setExp(0.03);
    }
    ret = setExpMode(req_set_exp_mode.mode());
    wsCommonResponse(CMD_CAMERA_TELE_SET_EXP_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    

    return ret;
}

int CamTele::getExpMode(WsPacket& ws_packet) {
    opMode_t mode;

    int ret = getExpMode(&mode);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_EXP_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, mode, ret);

    return ret;
}


int CamTele::setExp(WsPacket& ws_packet) {
    int ret;
    ReqSetExp req_set_exp;

    string serialized_data = ws_packet.data();
    if (!req_set_exp.ParseFromString(serialized_data)) {
        log_i("parse error");
        ret = -WS_PARSE_PROTOBUF_ERROR;
        wsCommonResponse(CMD_CAMERA_TELE_SET_EXP, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
    }

    double exp_value = getExpByIndex(exp_settings, req_set_exp.index());

    log_i("exp_value = %f", exp_value);

    if (exp_value > 0) {
        ret = setExp(exp_value);
    } else {
        ret = -WS_INVAID_PARAM;
    }

    wsCommonResponse(CMD_CAMERA_TELE_SET_EXP, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return 0;
}

int CamTele::getExp(WsPacket& ws_packet) {
    double exp;

    int ret = getExp(&exp);
    int index = findClosestExpIndex(exp_settings, exp);
    log_i("get exp index = %d", index);
    wsCommonResWithDouble(CMD_CAMERA_TELE_GET_EXP, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, exp, ret);

    return ret;
}

int CamTele::getExp(ExposureSetting& exp_setting) {
    double now_exp;

    int ret = getExp(&now_exp);
    if (ret < 0)
        return ret;

    int index = findClosestExpIndex(exp_settings, now_exp);
    double closest_exp = getExpByIndex(exp_settings, index);
    ret = setExp(closest_exp);
    if (ret < 0)
        return ret;

    exp_setting.index = index;
    exp_setting.value = closest_exp;
    exp_setting.name = getExpNameByIndex(exp_settings, index);

    log_i("get exp index = %d", exp_setting.index);
    log_i("get exp value = %f", exp_setting.value);
    log_i("get exp name = %s", exp_setting.name.c_str());
    
    return ret;
}

int CamTele::setGainMode(WsPacket& ws_packet) {
    // int ret;
    // ReqSetGainMode req_set_gain_mode;

    // string serialized_data = ws_packet.data();
    // if (!req_set_gain_mode.ParseFromString(serialized_data)) {
    //     return -WS_PARSE_PROTOBUF_ERROR;
    // }
    
    // ret = setGainMode(req_set_gain_mode.mode());
    // wsCommonResponse(CMD_CAMERA_TELE_SET_GAIN_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    // if (ret == 0) {
    //     wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, req_set_gain_mode.mode(), 1, 0, 0, 0);
    // }

    return 0;
}

int CamTele::getGainMode(WsPacket& ws_packet) {
    int mode;
    int ret = getGainMode(&mode);

    wsCommonResWithInt(CMD_CAMERA_TELE_GET_GAIN_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, mode, ret);

    return ret;
}


int CamTele::setGain(WsPacket& ws_packet) {
    int ret;
    ReqSetGain req_set_gain;

    string serialized_data = ws_packet.data();
    if (!req_set_gain.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = getIntValueByIndex(gain_settings, req_set_gain.index());
    if (value >= 0) {
        ret = setGain(value);
    } else {
        ret = -WS_INVAID_PARAM;
    }
    
    wsCommonResponse(CMD_CAMERA_TELE_SET_GAIN, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    

    return ret;
}


int CamTele::getGain(WsPacket& ws_packet) {
    int gain;
    
    int ret = getGain(&gain);
    int index = findIntValueIndex(gain_settings, gain);
    log_i("get gain index = %d", index);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_GAIN_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, gain, ret);

    return ret;
}

int CamTele::getGain(IntValueSetting& gain_setting) {
    int gain;

    int ret = getGain(&gain);
    if (ret < 0)
        return ret;

    int index = findClosestIntValueIndex(gain_settings, gain);
    int closest_gain = getIntValueByIndex(gain_settings, index);
    ret = setGain(closest_gain);
    if (ret < 0)
        return ret;

    gain_setting.index = index;
    gain_setting.value = closest_gain;

    log_i("get gain index = %d", gain_setting.index);
    log_i("get gain value = %d", gain_setting.value);
    
    return ret;
}

int CamTele::setBrightness(RK_U32 value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }
    log_i("set brightness to %d", value);
    ret = SAMPLE_COMM_ISP_SET_Brightness(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}

int CamTele::setBrightness(WsPacket& ws_packet) {
    int ret;
    ReqSetBrightness req_set_brightness;

    string serialized_data = ws_packet.data();
    if (!req_set_brightness.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    log_i("set brightness = %d", req_set_brightness.value());
    int value = (req_set_brightness.value() + 100) * 255.0 / 200;
    ret = setBrightness(value);
    wsCommonResponse(CMD_CAMERA_TELE_SET_BRIGHTNESS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 3, 1, 0, req_set_brightness.value());
    }

    return 0;
}

int CamTele::getBrightness(RK_U32 *value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_Brightness(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("isp get brightness = %d", *value);

    return ret;
}

int CamTele::getBrightness(WsPacket& ws_packet) {
    RK_U32 value;
    int ret = getBrightness(&value);

    wsCommonResWithInt(CMD_CAMERA_TELE_GET_BRIGHTNESS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamTele::setContrast(RK_U32 value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set contrast to %d", value);
    ret = SAMPLE_COMM_ISP_SET_Contrast(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}

int CamTele::setContrast(WsPacket& ws_packet) {
    int ret;
    ReqSetContrast req_set_contrast;

    string serialized_data = ws_packet.data();
    if (!req_set_contrast.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = (req_set_contrast.value() + 100) * 255.0 / 200;
    ret = setContrast(value);
    wsCommonResponse(CMD_CAMERA_TELE_SET_CONTRAST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 4, 1, 0, req_set_contrast.value());
    }

    return 0;
}

int CamTele::getContrast(RK_U32 *value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_Contrast(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("isp get contrast = %d", *value);

    return ret;
}

int CamTele::getContrast(WsPacket& ws_packet) {
    int ret;
    RK_U32 value;

    ret = getContrast(&value);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_CONTRAST, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamTele::setSaturation(RK_U32 value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set saturation to %d", value);
    ret = SAMPLE_COMM_ISP_SET_Saturation(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}

int CamTele::setSaturation(WsPacket& ws_packet) {
    int ret;
    ReqSetSaturation req_set_saturation;

    string serialized_data = ws_packet.data();
    if (!req_set_saturation.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = (req_set_saturation.value() + 100) * 255.0 / 200;
    ret = setSaturation(value);
    wsCommonResponse(CMD_CAMERA_TELE_SET_SATURATION, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 6, 1, 0, req_set_saturation.value());
    }

    return ret;
}

int CamTele::getSaturation(RK_U32 *value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_Saturation(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("isp get saturation = %d", *value);

    return ret;
}

int CamTele::getSaturation(WsPacket& ws_packet) {
    int ret;
    RK_U32 value;

    ret = getSaturation(&value);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_SATURATION, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamTele::setHue(RK_U32 value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set hue to %d", value);
    ret = SAMPLE_COMM_ISP_SET_Hue(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}

int CamTele::setHue(WsPacket& ws_packet) {
    int ret;
    ReqSetHue req_set_hue;

    string serialized_data = ws_packet.data();
    if (!req_set_hue.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = (req_set_hue.value() + 180) * 255.0 / 360;
    ret = setHue(value);
    wsCommonResponse(CMD_CAMERA_TELE_SET_HUE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 5, 1, 0, req_set_hue.value());
    }

    return ret;
}

int CamTele::getHue(RK_U32 *value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_Hue(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("isp get hue = %d", *value);

    return ret;
}

int CamTele::getHue(WsPacket& ws_packet) {
    int ret;
    RK_U32 value;

    ret = getHue(&value);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_HUE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamTele::setSharpness(RK_U32 value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    log_i("set sharpness to %d", value);
    ret = SAMPLE_COMM_ISP_SET_Sharpness(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;

    return ret;
}

int CamTele::setSharpness(WsPacket& ws_packet) {
    int ret;
    ReqSetSharpness req_set_sharpness;

    string serialized_data = ws_packet.data();
    if (!req_set_sharpness.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    ret = setSharpness(req_set_sharpness.value());
    wsCommonResponse(CMD_CAMERA_TELE_SET_SHARPNESS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 0, 7, 1, 0, req_set_sharpness.value());
    }

    return ret;
}

int CamTele::getSharpness(RK_U32 *value) {
    int ret;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    ret = SAMPLE_COMM_ISP_GET_Sharpness(0, value);
    ret = ret < 0 ? -CODE_CAMERA_TELE_ISP_SET_FAILED : ret;
    log_i("isp sharpness hue = %d", *value);

    return ret;
}

int CamTele::getSharpness(WsPacket& ws_packet) {
    int ret;
    RK_U32 value;

    ret = getSharpness(&value);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_SHARPNESS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamTele::setWBMode(WsPacket& ws_packet) {
    int ret;
    ReqSetWBMode req_set_wb_mode;

    string serialized_data = ws_packet.data();
    if (!req_set_wb_mode.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }
    opMode_t mode = (opMode_t)(req_set_wb_mode.mode());
    ret = setWBMode(mode);
    wsCommonResponse(CMD_CAMERA_TELE_SET_WB_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, req_set_wb_mode.mode(), 2, 2, 0, 0);
    }
    
    return ret;
}

int CamTele::getWBMode(WsPacket& ws_packet) {
   opMode_t mode;
   int ret = getWBMode(&mode);
   wsCommonResWithInt(CMD_CAMERA_TELE_SET_WB_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, mode, ret);
    
   return ret;
}

int CamTele::setWBScene(WsPacket& ws_packet) {
    int ret;
    ReqSetWBSence req_set_sence;

    string serialized_data = ws_packet.data();
    if (!req_set_sence.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    rk_aiq_wb_scene_t scene = (rk_aiq_wb_scene_t)(req_set_sence.value());
    ret = setWBScene(scene);
    wsCommonResponse(CMD_CAMERA_TELE_SET_WB_SCENE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, 1, 2, 2, req_set_sence.value(), 0);
    }

    return ret;
}

int CamTele::getWBScene(WsPacket& ws_packet) {
    rk_aiq_wb_scene_t scene = RK_AIQ_WBCT_FLUORESCENT;
    int ret = getWBScene(&scene);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_WB_MODE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, scene, ret);

    return ret;
}

int CamTele::setWBCT(WsPacket& ws_packet) {
    int ret;
    ReqSetWBCT req_set_wbct;

    string serialized_data = ws_packet.data();
    if (!req_set_wbct.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int value = getIntValueByIndex(wb_settings, req_set_wbct.index());
    if (value > 0) {
        ret = setWBCT(value);
    } else {
        ret = -WS_INVAID_PARAM;
    }
    wsCommonResponse(CMD_CAMERA_TELE_SET_WB_CT, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    if (ret == 0) {
        wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, true, 1, 2, 0, req_set_wbct.index(), 0);
    }

    return ret;
}

int CamTele::getWBCT(WsPacket& ws_packet) {
    RK_U32 ct;
    int ret = getWBCT(&ct);
    int index = findIntValueIndex(wb_settings, ct);
    log_i("get wbct index = %d", index);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_WB_SCENE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ct, ret);

    return ret;
}

int CamTele::setIrCut(WsPacket& ws_packet) {
    int ret;
    ReqSetIrCut req_set_ircut;

    string serialized_data = ws_packet.data();
    if (!req_set_ircut.ParseFromString(serialized_data)) {
        log_i("parse error");
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    ret = setIrCut(req_set_ircut.value());
    wsCommonResponse(CMD_CAMERA_TELE_SET_IRCUT, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return ret;
}

int CamTele::getIrCut(WsPacket& ws_packet) {
    int value;
    
    int ret = getIrCut(&value);
    wsCommonResWithInt(CMD_CAMERA_TELE_GET_IRCUT, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, value, ret);

    return ret;
}

int CamTele::startTimeLapsePhotograph(WsPacket& ws_packet) {
    int ret;
    ReqStartTimeLapse req_start_timelapse;

    unique_lock<mutex> lock(camera_mutex);
    string serialized_data = ws_packet.data();
    if (!req_start_timelapse.ParseFromString(serialized_data)) {
        log_i("protobuf parse error");
        wsCommonResponse(CMD_CAMERA_TELE_STOP_TIMELAPSE_PHOTO, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, -WS_PARSE_PROTOBUF_ERROR);
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int fps = 30;
    timelapse_interval = getTimelapseInterval();
    int total_time = getTimelapseTotalTime();
    if (total_time == 0)
        total_time = INT_MAX;
    int out_time = total_time / (30.0 * timelapse_interval);
    
    if ((ret = isCameraOpen()) < 0) {
        state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_FAIL);
        wsCommonResponse(CMD_CAMERA_TELE_STOP_TIMELAPSE_PHOTO, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_FAIL);
        wsCommonResponse(CMD_CAMERA_TELE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, -WS_SDCARD_NOT_EXIST);
        return -WS_SDCARD_NOT_EXIST;
    }

    try {
        if (!fs::exists(album_path + video_path))
            fs::create_directory(album_path + video_path);
    } catch (const fs::filesystem_error& ex) {
        log_e("Error creating directories: %s", ex.what());
        state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_FAIL);
        wsCommonResponse(CMD_CAMERA_TELE_STOP_TIMELAPSE_PHOTO, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, -WS_SDCARD_WRITE_ERROR);
        return -WS_SDCARD_WRITE_ERROR;
    }

    if (getTimelapseState() == CAM_TIMELAPSE_RECORDING) {
        log_e("cam tele is timelapse recording");
        wsCommonResponse(CMD_CAMERA_TELE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, WS_OK);
        return 0;
    }
    
    setTimelapseState(CAM_TIMELAPSE_RECORDING);
    lock.unlock();
    log_i("start timelapse photo, timelapse_interval = %d, total_time = %d", timelapse_interval, total_time);

    wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_RUNNING);
    state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_WORKING_STATE);

    timelapse_timer_manager.startTimer([&](int elapsed_time) {
        log_i("out time = %d", elapsed_time);
        timelapse_elapsed_time = elapsed_time;
        timelapse_out_time = elapsed_time / (30 * timelapse_interval);
        wsResNotifyTimelapseOutTime(CMD_NOTIFY_TELE_TIMELAPSE_OUT_TIME, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, timelapse_interval, timelapse_out_time, elapsed_time);
    }, 1);

    string file_name = getTimeStamp();
    string video_path_h264 = album_path + video_path + timelapse_name_prefix + file_name + ".h264";
    string video_path_mp4 = album_path + video_path + timelapse_name_prefix + file_name + ".mp4";
    log_i("video path h264 = %s, mp4 = %s", video_path_h264.c_str(), video_path_mp4.c_str());

    camTeleStartEncoder(video_path_h264.c_str(), timelapse_interval, out_time, b_binning);

    if (getTimelapseState() == CAM_TIMELAPSE_IDLE) {  // 主动停止
        wsCommonResponse(CMD_CAMERA_TELE_STOP_TIMELAPSE_PHOTO, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, 0);
    } else {
        wsCommonResponse(CMD_CAMERA_TELE_START_TIMELAPSE_PHOTO, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, 0);
    }

    setTimelapseState(CAM_TIMELAPSE_IDLE);
    timelapse_timer_manager.stopTimer();

    wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_IDLE);
    state_manager->rgb_power_.setRgbEffect(RGB_EFFECT_SUCCESS);

    record_loading_count += 1;
    std::thread h264_to_mp4([video_path_h264, video_path_mp4]() {
        string cmd_h264tomp4 = "ffmpeg -f h264 -i " + video_path_h264 + " -vcodec copy " + video_path_mp4;
        system(cmd_h264tomp4.c_str());

        string cmd_delH264 = "rm " + video_path_h264;
        system(cmd_delH264.c_str());
        system("sync");
        m_album.album_video_.updateVideo(video_path_mp4);
        record_loading_count -= 1;
        wsNotifyAlbumUpdate(CMD_NOTIFY_ALBUM_UPDATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, MEDIA_VIDEO); 
    });
    h264_to_mp4.detach();

    return 0;
}

int CamTele::stopTimeLapsePhotograph() {
    log_i("stop timelapse photo");
    wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_STOPPING);
    setTimelapseState(CAM_TIMELAPSE_STOPPING);

    return 0;
}

int CamTele::stopTimeLapsePhotograph(WsPacket& ws_packet) {
    stopTimeLapsePhotograph();

    return 0;
}

int CamTele::getAllParams() {
    int ret;
    double exp_value;
    opMode_t exp_mode;
    int exp_index = 75;
    int gain_mode = 1;
    int gain_value = 0;
    int gain_index = 0;
    int ircut_value = 0;
    opMode_t wb_mode = (opMode_t)0;
    RK_U32 wb_ct_value = 0;
    int wb_ct_index = -1;
    rk_aiq_wb_scene_t wb_scene_index = (rk_aiq_wb_scene_t)-1;
    RK_U32 brightness = 0, contrast = 0, hue = 0, saturation = 0, sharpness = 0;
    int brightness_mapping_value = 0, contrast_mapping_value = 0, hue_mapping_value = 0, saturation_mapping_value = 0, sharpness_mapping_value = 0;

    unique_lock<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        lock.unlock();

        exp_mode = (opMode_t)1;
        getExp(&exp_value);
        exp_index = findClosestExpIndex(exp_settings, exp_value);
        gain_mode = 1;
        getGain(&gain_value);
        gain_index = findIntValueIndex(gain_settings, gain_value);
        if (gain_index == -1)
            gain_index = 0;
        getIrCut(&ircut_value);


        wsResGetAllParams(CMD_CAMERA_TELE_GET_ALL_PARAMS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, 0, exp_mode, exp_index, gain_mode, gain_index,
                        ircut_value, wb_mode, wb_ct_index, wb_scene_index, brightness, contrast, hue, saturation, sharpness, 30);

        return 0;
    }
    
    lock.unlock();
    getExpMode(&exp_mode);
    getExp(&exp_value);
    exp_index = findClosestExpIndex(exp_settings, exp_value);
    getGainMode(&gain_mode);
    getGain(&gain_value);
    gain_index = findClosestIntValueIndex(gain_settings, gain_value);
    if (gain_index == -1)
        gain_index = 0;
    getIrCut(&ircut_value);
    getWBMode(&wb_mode);
    getWBCT(&wb_ct_value);
    wb_ct_index = findClosestIntValueIndex(wb_settings, wb_ct_value);
    getWBScene(&wb_scene_index);
    if (wb_scene_index > 10)
        wb_scene_index = (rk_aiq_wb_scene_t)-1;
    getBrightness(&brightness);
    brightness_mapping_value = brightness * 200.0 / 255 - 100;
    getContrast(&contrast);
    contrast_mapping_value = contrast * 200.0 / 255 - 100;

    getHue(&hue);
    hue_mapping_value = hue * 360.0 / 255 - 180;
    getSaturation(&saturation);
    saturation_mapping_value = saturation * 200.0 / 255 - 100;
    getSharpness(&sharpness);
    int jpg_quality = getJpgQuality();

    wsResGetAllParams(CMD_CAMERA_TELE_GET_ALL_PARAMS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret, exp_mode, exp_index, gain_mode, gain_index,
                    ircut_value, wb_mode, wb_ct_index, wb_scene_index, brightness_mapping_value, contrast_mapping_value, hue_mapping_value, saturation_mapping_value, sharpness, jpg_quality);
    
    log_i("exp_mode = %d", exp_mode);
    log_i("exp_index = %d", exp_index);
    log_i("gain_mode = %d", gain_mode);
    log_i("gain_index = %d", gain_index);
    log_i("ircut_value = %d", ircut_value);
    log_i("wb_mode = %d", wb_mode);
    log_i("wb_ct_index = %d", wb_ct_index);
    log_i("wb_scene_index = %d", wb_scene_index);
    log_i("brightness = %d", brightness);
    log_i("contrast = %d", contrast);
    log_i("hue = %d", hue);
    log_i("saturation = %d", saturation);
    log_i("sharpness = %d", sharpness);
    log_i("jpg_quality = %d", jpg_quality);

    return 0;
}

int CamTele::getAllParams(WsPacket& ws_packet) {
    getAllParams();

    return 0;
}

int CamTele::setAllParams(int exp_mode, int exp_index, int gain_mode, int gain_index, int ircut_value, int wb_mode,
                            int wb_index_type, int wb_index, int brightness, int contrast, int hue, int saturation, int sharpness, int jpg_quality) {
    
    int ret;

    log_i("exp_mode = %d", exp_mode);
    log_i("exp_index = %d", exp_index);
    log_i("gain_mode = %d", gain_mode);
    log_i("gain_index = %d", gain_index);
    log_i("ircut_value = %d", ircut_value);
    log_i("wb_mode = %d", wb_mode);
    log_i("wb_index = %d", wb_index);
    log_i("brightness = %d", brightness);
    log_i("contrast = %d", contrast);
    log_i("hue = %d", hue);
    log_i("saturation = %d", saturation);
    log_i("sharpness = %d", sharpness);

    unique_lock<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        wsCommonResponse(CMD_CAMERA_TELE_SET_ALL_PARAMS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }
    
    lock.unlock();
    setExpMode(exp_mode);
    usleep(100 * 1000);
    double exp_value = getExpByIndex(exp_settings, exp_index);
    if (exp_value > 0) {
        ret = setExp(exp_value);
    } else {
        return -WS_INVAID_PARAM;
    }

    setGainMode(gain_mode);
    int gain_value = getIntValueByIndex(gain_settings, gain_index);
    if (gain_value > 0) {
        ret = setGain(gain_value);
    } else {
        ret = -WS_INVAID_PARAM;
    }

    setIrCut(ircut_value);

    setWBMode((opMode_t)wb_mode);

    if (wb_mode == 1 && wb_index_type == 0) { //手动，色温模式
        int wb_value = getIntValueByIndex(wb_settings, wb_index);
        if (wb_value > 0) {
            ret = setWBCT(wb_value);
        } else {
            ret = -WS_INVAID_PARAM;
        }
    }
    if (wb_mode == 1 && wb_index_type == 1) { //手动，白平衡场景模式
        rk_aiq_wb_scene_t wb_scene = (rk_aiq_wb_scene_t)wb_index;
        setWBScene(wb_scene);
    }
    
    setBrightness((RK_U32)brightness);
    setContrast((RK_U32)contrast);
    setSaturation((RK_U32)saturation);
    setHue((RK_U32)hue);
    setSharpness((RK_U32)sharpness);
    setJpgQuality(jpg_quality);

   return ret;                             

}

int CamTele::setAllParams(WsPacket& ws_packet) {
    int ret;
    ReqSetAllParams req_set_all_params;
    string serialized_data = ws_packet.data();

    if (!req_set_all_params.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int exp_mode = req_set_all_params.exp_mode();
    int exp_index = req_set_all_params.exp_index();
    int gain_mode = req_set_all_params.gain_mode();
    int gain_index = req_set_all_params.gain_index();
    int ircut_value = req_set_all_params.ircut_value();
    int wb_mode = req_set_all_params.wb_mode();
    int wb_index = req_set_all_params.wb_index();
    int wb_index_type = req_set_all_params.wb_index_type();
    int brightness = req_set_all_params.brightness();
    int contrast = req_set_all_params.contrast();
    int hue = req_set_all_params.hue();
    int saturation = req_set_all_params.saturation();
    int sharpness = req_set_all_params.sharpness();
    int jpg_quality = req_set_all_params.jpg_quality();

    ret = setAllParams(exp_mode, exp_index, gain_mode, gain_index, ircut_value, wb_mode, wb_index_type, wb_index, brightness, contrast, hue, saturation, sharpness, jpg_quality);

    wsCommonResponse(CMD_CAMERA_TELE_SET_ALL_PARAMS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return 0;
}

int CamTele::startCaptureRaw(WsPacket& ws_packet) {
    return 0;
}

int CamTele::startCaptureRaw(const cv::Size& img_size, int img_to_take) {
    b_capture_raw = true;

    // 先关摄像头
    closeCamera();

    unique_lock<mutex> lock(camera_mutex);

    if (isCameraClose() < 0) {
        log_e("cam tele is busy");
        return -CODE_CAMERA_TELE_WORKING_BUSY;
    }

    if (getPhotoState() != CAM_TELE_PHOTO_IDLE) {
        log_e("cam tele is busy");
        return -CODE_CAMERA_TELE_WORKING_BUSY;
    }

    if (checkSdExist() < 0) {
        log_e("check tfcard not exit");
        return -WS_SDCARD_NOT_EXIST;
    }

    setPhotoState(CAM_TELE_PHOTO_RAW);

    system("echo 0 > /sys/devices/platform/rkcif_mipi_lvds/compact_test");
    std::ostringstream cmd;
    cmd << "media-ctl -d /dev/media0 --set-v4l2 '\"m00_f_imx415 1-001a\":0[fmt:SGBRG12_1X12/" << img_size.width << "x" << img_size.height << "]'";
    system(cmd.str().c_str());

    // 打开设备
    fd_raw_ = open("/dev/video0", O_RDWR);
    if (fd_raw_ == -1) {
        log_i("open raw camera failed");
        setPhotoState(CAM_TELE_PHOTO_IDLE);
        return -1;
    }

    int ret = 0;

    // 设置图像参数
    memset(&vformat, 0, sizeof(vformat));
    vformat.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    vformat.fmt.pix.pixelformat = V4L2_PIX_FMT_SGBRG12;
    vformat.fmt.pix.width = img_size.width;
    vformat.fmt.pix.height = img_size.height;
    if (-1 == ioctl(fd_raw_, VIDIOC_S_FMT, &vformat)) {
        log_e("VIDIOC_S_FMT:%s", strerror(errno));
        ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
        goto __exit;
    }

    // 申请帧缓存区
    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    reqbuf.count = 3;
    if (-1 == ioctl(fd_raw_, VIDIOC_REQBUFS, &reqbuf)) {
        log_e("VIDIOC_REQBUFS:%s", strerror(errno));
        ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
        goto __exit;
    }

    // 分配缓存区
    buffers = (data_buffer*)calloc(reqbuf.count, sizeof(*buffers));
    if (buffers == NULL) {
        log_e("buffers is NULL");
        ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
        goto __exit;
    }

    // mmap内存映射
    for (int i = 0; i < (int)reqbuf.count; i++) {
        memset(planes, 0, sizeof(planes));
        memset(&buffer, 0, sizeof(buffer));
        buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        buffer.memory = V4L2_MEMORY_MMAP;
        buffer.index = i;
        buffer.length = 1;
	    buffer.m.planes = planes;

        if (-1 == ioctl(fd_raw_, VIDIOC_QUERYBUF, &buffer)) {
            log_e("VIDIOC_QUERYBUF:%s", strerror(errno));
            ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
            goto __exit;
        }

        buffers[i].length = buffer.m.planes[0].length;

        buffers[i].start = mmap(NULL, buffer.m.planes[0].length, PROT_READ | PROT_WRITE,
                                MAP_SHARED, fd_raw_, buffer.m.planes[0].m.mem_offset);

        if (MAP_FAILED == buffers[i].start) {
            log_e("mmap:%s", strerror(errno));
            ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
            goto __exit;
        }

        if (ioctl(fd_raw_, VIDIOC_QBUF, &buffer) == -1) {
            log_e("VIDIOC_QBUF failed:%s", strerror(errno));
            ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
            goto __exit;
        }
    }

    // 打开视频流
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    if (ioctl(fd_raw_, VIDIOC_STREAMON, &type) == -1) {
        log_e("VIDIOC_STREAMON failed:%s", strerror(errno));
        ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
        goto __exit;
    }

    // 初始化完成
    lock.unlock();

    int img_count;
    img_count = 0;

    time_checker timer;

    // 从队列拿图
    while(b_capture_raw) {
        memset(planes, 0, sizeof(planes));
        memset(&buffer, 0, sizeof(buffer));
        buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        buffer.memory = V4L2_MEMORY_MMAP;
        buffer.length = 1;
        buffer.m.planes = planes;

        if (ioctl(fd_raw_, VIDIOC_DQBUF, &buffer) == -1) {
            log_e("GetFrame VIDIOC_DQBUF Failed:%s", strerror(errno));
            goto __exit;
        }

        timer.start();
        // log_i("get v4l2 buffers[%d]", buffer.index);
        
        if(buffer.m.planes[0].bytesused == img_size.width * img_size.height * sizeof(ushort)) {
            // 回调函数，其他模块自行处理存图等
            if (captureRawCallback_) {
                cv::Mat img(img_size, CV_16UC1, buffers[buffer.index].start);
                std::string date_obs = steadyTimeToSystemTime(buffer.timestamp);

                // 与存图时间重叠，不影响整体拍摄进度
                std::thread set_img_raw_thread([&]() {
                    setImgRaw(img);  // 拿RAW图用于日月跟踪补偿
                });

                ret = captureRawCallback_(img, date_obs, img_count);

                set_img_raw_thread.join();

                if (ret) {
                    break;
                }
            }
        }
        else 
            log_i("bytesused not compatible");

        timer.stop();
        timer.show_distance("Update stage ---- captureRawCallback_ cost");

        timer.start();

        img_count++;

        if (ioctl(fd_raw_, VIDIOC_QBUF, &buffer) < 0) {
            log_e("GetFrame VIDIOC_QBUF Failed:%s", strerror(errno));
            goto __exit;
        }

        if(img_count >= img_to_take)
            break;
    }

    // 关闭视频流
    if (ioctl(fd_raw_, VIDIOC_STREAMOFF, &type) == -1) {
        log_e("VIDIOC_STREAMOFF failed:%s", strerror(errno));
        ret = -CODE_CAMERA_TELE_CAPTURE_RAW_FAILED;
        goto __exit;
    }

__exit:
    // 释放资源
    exitCaptureRaw();

    b_capture_raw = false;
    
    setPhotoState(CAM_TELE_PHOTO_IDLE);

    img_raw_cv.notify_all();  // 退出拿RAW图线程

    return ret;
}

void CamTele::exitCaptureRaw() {
    // 手动释放分配的内存
    if(buffers != NULL) {
        for (int i = 0; i < (int)reqbuf.count; i++) {
            if (-1 == munmap(buffers[i].start, buffers[i].length)) {
                log_e("munmap:%s", strerror(errno));
            }
        }
            
        for (int i = 0; i < 5; i++) {
            struct v4l2_requestbuffers reqbuf_tmp = {0};
            reqbuf_tmp.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
            reqbuf_tmp.memory = V4L2_MEMORY_MMAP;
            reqbuf_tmp.count = 0;
            if (-1 == ioctl(fd_raw_, VIDIOC_REQBUFS, &reqbuf_tmp)) {
                log_e("VIDIOC_REQBUFS:%s", strerror(errno));
                usleep(50 * 1000);
            } else {
                break;
            }
        }

        xfree(buffers);
    }

    close(fd_raw_);
    fd_raw_ = -1;
}

void CamTele::stopCaptureRaw() {
    b_capture_raw = false;

    if (fd_raw_ < 0)
        return;

    // 关闭视频流
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    if (ioctl(fd_raw_, VIDIOC_STREAMOFF, &type) == -1) {
        log_e("VIDIOC_STREAMOFF failed:%s", strerror(errno));
    }
}

const cv::Size& CamTele::imgSize2k() {
    return img_size_2k;
}

const cv::Size& CamTele::imgSize4k() {
    return img_size_4k;
}

int setImgRaw(const cv::Mat& img_raw) {
    std::lock_guard<std::mutex> lock(img_raw_mutex);
    img_raw_ = img_raw.clone();  // 只是clone，处理放到获取的地方，减少对拍图的影响
    img_raw_cv.notify_all();

    return 0;
}

int getProcessedRaw(cv::Mat& processed_raw_8b) {
    std::unique_lock<std::mutex> lock(img_raw_mutex);
    img_raw_cv.wait(lock, [&] { return !img_raw_.empty() || !b_capture_raw; });

    if (!b_capture_raw) 
        return -1;

    cv::Mat img_raw;
    img_raw = img_raw_.clone();
    img_raw_.release();  // 读完后清空

    lock.unlock();

    cv::Mat processed_raw;
    Preprocessor::readLightFrame(img_raw, processed_raw);
    processed_raw.convertTo(processed_raw_8b, CV_8UC3, 1.0 / 256);

    return 0;
}

int CamTele::setJpgQuality(int jpg_quality) {
    log_i("set jpg quallity = %d", jpg_quality);
    int ret = 0;

    lock_guard<mutex> lock(camera_mutex);
    if ((ret = isCameraOpen()) < 0) {
        return ret;
    }

    jpeg_preview_quality = jpg_quality;
    if (jpg_quality <= 30)
        jpeg_preview_quality = 30;
    if (jpg_quality >= 85)
        jpeg_preview_quality = 85;

    VENC_JPEG_PARAM_S st_jpeg_param;
    st_jpeg_param.u32Qfactor = jpeg_preview_quality;
    RK_MPI_VENC_SetJpegParam(VENC_CHANNEL_JPEG_PREVIEW, &st_jpeg_param);

    wsResNotifyParam(CMD_NOTIFY_TELE_SET_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, false, 1, 9, 1, 0, jpg_quality);
    return ret;
}

int CamTele::getJpgQuality() {
    log_i("jpg quallity = %d", jpeg_preview_quality);
    return jpeg_preview_quality;
}

//TODO:
int CamTele::setJpgQuality(WsPacket& ws_packet) {
    int ret = 0;
    ReqSetJpgQuality req_set_jpg_quality;
    string serialized_data = ws_packet.data();

    if (!req_set_jpg_quality.ParseFromString(serialized_data)) {
        ret = -WS_PARSE_PROTOBUF_ERROR;
        wsCommonResponse(CMD_CAMERA_TELE_SET_JPG_QUALITY, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);
        return ret;
    }

    int jpg_quality = req_set_jpg_quality.quality();
    log_i("setJpgQuality = %d", jpg_quality);
    ret = setJpgQuality(jpg_quality);

    wsCommonResponse(CMD_CAMERA_TELE_SET_JPG_QUALITY, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, ret);

    return 0;
}

int CamTele::setFeatureParam(WsPacket& ws_packet) {
    int ret;
    ReqSetFeatureParams req_set_feature_params;
    string serialized_data = ws_packet.data();

    if (!req_set_feature_params.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    lock_guard<mutex> lock(feature_param_mutex);

    int id = req_set_feature_params.param().id();
    log_i("param id = %d", id);
    switch (id) {
        case 0: {
            int astro_binning = req_set_feature_params.param().index();
            log_i("set astro binning index = %d", astro_binning);
            state_manager->astro_.setCaptureLightBinning(astro_binning);
            break;
        }
        case 1: {
            int astro_img_to_take = req_set_feature_params.param().continue_value();
            log_i("set astro_img_to_take = %d", astro_img_to_take);
            state_manager->astro_.setCaptureLightCount(astro_img_to_take);
            break;
        }
        case 2: {
            int astro_format = req_set_feature_params.param().index();
            log_i("set astro_format index = %d", astro_format);
            state_manager->astro_.setCaptureLightFormat(astro_format);
            break;
        }
        case 3: {
            WsVersion ws_version(ws_packet.major_version(), ws_packet.minor_version());
            if (ws_version.isGreaterThan(1, 1)) {
                log_i("set burst count index = %d", req_set_feature_params.param().index());
                setBurstCountIndex(req_set_feature_params.param().index());
                burst_photo_count = getBurstCountNew();
            } else {
                burst_photo_count = req_set_feature_params.param().continue_value();
                log_i("set burst photo count = %d", burst_photo_count);
                setBurstPhotoCount(burst_photo_count);
            }
            
            break;
        }
        case 4: {
            log_i("set timelapse interval index = %d", req_set_feature_params.param().index());
            setTimelapseIntervalIndex(req_set_feature_params.param().index());
            break;
        }
        case 5: {
            log_i("set timelapse total time index = %d", req_set_feature_params.param().index());
            setTimelapseTotalTimeIndex(req_set_feature_params.param().index());
            break;
        }
        case 6: {
            int rows = req_set_feature_params.param().continue_value();
            log_i("set panorama rows = %d", rows);
            setPanoramaRow(rows);
        }
            break;
        case 7:
            {
                int cols = req_set_feature_params.param().continue_value();
                log_i("set panorama cols = %d", cols);
                setPanoramaCol(cols);
            }
            break;
        case 8: {
            int astro_display_source = req_set_feature_params.param().index();
            log_i("set astro_display_source index = %d", astro_display_source);
            state_manager->astro_.setCaptureLightDisplaySource(astro_display_source);
            break;
        }
        case 9: {
            log_i("set burst interval index = %d", req_set_feature_params.param().index());
            setBurstIntervalIndex(req_set_feature_params.param().index());
            burst_photo_interval = getBurstInterval();
            log_i("burst_photo_interval = %d", burst_photo_interval);
            break;
        }
    }

    wsResNotifyParam(CMD_NOTIFY_SET_FEATURE_PARAM, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, 
                    req_set_feature_params.param().hasauto(), req_set_feature_params.param().auto_mode(), req_set_feature_params.param().id(), 
                    req_set_feature_params.param().mode_index(), req_set_feature_params.param().index(), req_set_feature_params.param().continue_value());

    wsCommonResponse(CMD_CAMERA_TELE_SET_FEATURE_PARAM, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, WS_OK);

    return 0;
    
}

int CamTele::getAllFeatureParams(WsPacket& ws_packet) {
    int astro_binning = state_manager->astro_.getCaptureLightBinning();
    int astro_img_to_take = state_manager->astro_.getCaptureLightCount();
    int astro_format = state_manager->astro_.getCaptureLightFormat();
    int astro_display_source = state_manager->astro_.getCaptureLightDisplaySource();
    int cur_panorama_row = getPanoramaRow();
    int cur_panorama_col = getPanoramaCol();
    int cur_timelapse_interval_index = getTimelapseIntervalIndex();
    int cur_timelapse_total_time = getTimelapseTotalTimeIndex();
    int cur_burst_count_index = getBurstCountIndex();
    int cur_burst_interval_index = getBurstIntervalIndex();
    log_i("cur_burst_count_index = %d", cur_burst_count_index);
    log_i("burst_photo_count = %d", burst_photo_count);

    WsVersion ws_version(ws_packet.major_version(), ws_packet.minor_version());

    wsResGetAllFeatureParams(CMD_CAMERA_TELE_GET_ALL_FEATURE_PARAMS, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, WS_OK, 
                            astro_binning, astro_img_to_take, astro_format, burst_photo_count, cur_timelapse_interval_index, cur_timelapse_total_time, cur_panorama_row, cur_panorama_col, astro_display_source,
                             cur_burst_count_index, cur_burst_interval_index, ws_version);

    return 0;
}

int CamTele::getSystemWorkingState(WsPacket& ws_packet) {
    state_manager->astro_.notifyAstroStateAndProgress();

    if (getRecordState() == CAM_RECORDING) {
         wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_RECORD, STATE_RUNNING);
         wsResNotifyRecordTime(CMD_NOTIFY_TELE_RECORD_TIME, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, record_time);
    }

    if (getTimelapseState() == CAM_TIMELAPSE_RECORDING) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_RUNNING);
        wsResNotifyTimelapseOutTime(CMD_NOTIFY_TELE_TIMELAPSE_OUT_TIME, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, timelapse_interval, timelapse_out_time, timelapse_elapsed_time);
    } else if (getTimelapseState() == CAM_TIMELAPSE_STOPPING) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_STOPPING);
        wsResNotifyTimelapseOutTime(CMD_NOTIFY_TELE_TIMELAPSE_OUT_TIME, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, timelapse_interval, timelapse_out_time, timelapse_elapsed_time);
    }

    if (state_manager->cam_wide_.getTimelapseState() == CAM_WIDE_TIMELAPSE_RECORDING) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_TIMELAPSE, STATE_RUNNING);
        wsResNotifyTimelapseOutTime(CMD_NOTIFY_WIDE_TIMELAPSE_OUT_TIME, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, state_manager->cam_wide_.getSessionTimelapseInterval(), state_manager->cam_wide_.getTimelapseOutTime(), state_manager->cam_wide_.getTimelapseElapsedTime());
    }

    if (getPhotoState() == CAM_TELE_PHOTO_BURST) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_BURST, STATE_RUNNING);
        wsResNotifyBurstProgress(CMD_NOTIFY_TELE_BURST_PROGRESS, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, burst_photo_count, burst_photo_id);
    }

    if (state_manager->cam_wide_.getPhotoState() == CAM_WIDE_PHOTO_BURST) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_BURST, STATE_RUNNING);
        wsResNotifyBurstProgress(CMD_NOTIFY_WIDE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, state_manager->cam_wide_.getSessionBurstPhotoCount(), state_manager->cam_wide_.getBurstPhotoId());
    }

    if (getPanoramaState() == CAM_PANORAMA_PHOTOGRAPHING) {
        wsResNotifyCamFunctionState(CMD_NOTIFY_TELE_FUNCTION_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, CAM_FUNCTION_PANORAMA, STATE_RUNNING);
        state_manager->panorama_.notifiyPhotoProgress();
    }

    state_manager->focus_.notifyFocusState();

    state_manager->tracker_.notifyTrackState();

    int ws_mode = getWsHostSlaveModeById(ws_packet.client_id());
    bool ws_master_lock = getWsLockMaster();
    log_i("ws mode = %d, ws_master_lock = %d", ws_mode, ws_master_lock);
    wsResNotifyHostSlaveMode(CMD_NOTIFY_WS_HOST_SLAVE_MODE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, ws_mode, ws_packet.client_id(), ws_master_lock);

    // sd卡容量
    state_manager->system_.getSdCardInfo(0);

    // 电量
    int cur_ele = state_manager->rgb_power_.getEle();
    wsCommonResWithInt(CMD_NOTIFY_ELE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, cur_ele, 0);

    // 充电状态
    int charge_state = state_manager->rgb_power_.getChargeState();
    if (charge_state != CHARGE_NONE)
        wsCommonResWithInt(CMD_NOTIFY_CHARGE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, charge_state, 0);

    // 环形灯状态
    int rgb_state = state_manager->rgb_power_.getRgbState();
    log_i("rgb state = %d", rgb_state);
    wsResNotifyRgbState(CMD_NOTIFY_RGB_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, rgb_state);

    // 电量指示灯状态
    int power_ind_state = state_manager->rgb_power_.getPowerIndState();
    log_i("power ind state = %d", power_ind_state);
    wsResNotifyPowerIndState(CMD_NOTIFY_POWER_IND_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, power_ind_state);

    // MTP状态通知
    int mtp_state = state_manager->system_.getMTPState();
    log_i("mtp state = %d", mtp_state);
    wsResNotifyMTPState(CMD_NOTIFY_MTP_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, mtp_state);

    // 长焦广角画面匹配
    notifyPictureMatchingResult();

    // CPU mode
    int cpu_mode = state_manager->system_.getCpuMode();
    log_i("cpu mode = %d", cpu_mode);
    wsResNotifyMTPState(CMD_NOTIFY_CPU_MODE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, cpu_mode);

    wsCommonResponse(CMD_CAMERA_TELE_GET_SYSTEM_WORKING_STATE, MODULE_CAMERA_TELE, CMD_TYPE_RESPONSE, WS_OK);

    return 0;
}

int CamTele::notifyPictureMatchingResult() {
    // double wide_fv_w = 38.9, wide_fv_h = 22.4, tele_fv_w = 3.188, tele_fv_h = 1.794;

    // int tele_in_wide_w = 1280 * tele_fv_w / wide_fv_w;
    // int tele_in_wide_h = 720 * tele_fv_h / wide_fv_h;
    // int left_top_w = 640 - tele_in_wide_w / 2;
    // int left_top_h = 360 - tele_in_wide_h / 2;

    // log_i("tele_in_wide_w = %d, tele_in_wide_h = %d", tele_in_wide_w, tele_in_wide_h);
    // log_i("left_top_w = %d, left_top_h = %d, w = %d, h = %d", left_top_w, left_top_h, tele_in_wide_w, tele_in_wide_h);
    
    templateRect template_rect = state_manager->focus_.getCurTemplateRect();
    wsResNotifyPictureMatching(CMD_NOTIFY_TELE_WIDI_PICTURE_MATCHING, MODULE_FOCUS, CMD_TYPE_NOTIFICATION, 0, template_rect.left_top_w, template_rect.left_top_h, template_rect.width, template_rect.height, 0.99);
    
    return 0;
}

int CamTele::getRecordLoadingCount() {
    return record_loading_count;
}